PNG vs SVG for sprites
codepen.io
codepen.io
The browser will generate an internal bitmap for each decoded PNG image, and uses that for blitting/compositing. Any decent browser ought do the same for SVG icons as well (and just invalidate the bitmap when the SVG image size/rotation or page zooming changes). When compared to blitting, stroking and filling paths is expensive and so is PNG decoding.
Of course, SVG can be rotated and scaled and animated nicely, unlike PNG. That will obviously be slower if done every frame. But this isn't the case and shouldn't be the case for such static icons.
(Of course, the current state of affairs probably is as described on the demo page. But it doesn't need to be.)
The problem is SVG filters let you do some pretty cool compositing stuff, but you can't necessarily generalize those effects in a static image. For instance, you could blur elements on the page using an SVG blur filter overlayed over your html elements, but if you're just blitting from a static buffer (like with a PNG), it's going to be wrong, so now you're not just paying attention to if the SVG changes, you also have to pay attention to if anything underneath it or around it changed. It's not an impossible problem, but I think you could still call a browser decent if it doesn't bother with that sort of specialized optimization.
See: http://www.w3.org/TR/SVG/filters.html#AccessingBackgroundIma...
This is true for every single DOM element, which can layer and obscure and filter and be additive/subtractive and can have levels of translucency. This is not a novel problem with SVG, but it's one where the appropriate solution is a simple
if (isComplexCase) { // do uglier, more processor intensive activities } else { // do things the short and simple way }
The universe of possibilities on an artifact don't prescribe a baseline performance level unless you're actually using them.
And just to be clear, I don't fault the browser makers at all regarding this (any of us can contribute to Chromium/Webkit/Firefox), as they are simply optimizing their efforts towards the things that yield the biggest return, and optimizing SVG just isn't that critical. Yet this leads to a circular situation where we endlessly hear that you shouldn't use SVG because performance suffers.
I'd love to see some of the new vector map detail algorithms applied to SVG - the ones where you throw away any detail smaller than a pixel. Then we'd be able to zoom smoothly or (if the points need to be calculated and cached) progressively loading (and possibly streaming) detail.
http://research.microsoft.com/en-us/um/people/cloop/LoopBlin...
If someone like Ben Constable is lurking in this thread, he'd be able to say more this; I am extremely ignorant about this problem.
This demo is symptomatic of a general and irritating trend with modern Chrome and Firefox: the development teams seem to be very interested in doing enough to tick a box claiming they support a certain feature, but the quality of implementation can still be poor, sometimes to the point of being unusable as in this case. SVG is a common culprit, but for example typography in Chrome is another repeat offender.
It seems absurd that on a fairly high-spec PC I’m using to write this, Firefox running this simple SVG demo consumes more system resources than Maya doing production-quality 3D rendering. But something seems to have gone wrong with Firefox specifically a few months ago, because simply scrolling up and down a page now seems to be enough to ramp a workstation GPU up to full speed, and consequently just browsing a few pages using Firefox for half an hour can have the GPU fans running fast enough to be loud and the UPS showing a noticeably increase in power consumption for the workstation. Unfortunately, I’m not sure what could possibly explain this or exactly when it started happening, so it’s difficult to file any sort of useful bug report. :-(
That's development in general. Is it not? It's hard to find houses that don't have a focus on stuff like that. As averse as I am to it, I also have to admit it is a good thing. It's just embarrassing when the covers come off and the dirty hacks are revealed.
Luckily the hardware vendors are already working on fixing this. Here's an example of a particularly problematic scene rendered using NVIDIA's GPU-Accelerated Path Rendering, compared to Skia, Cairo, QT and Direct2D:
https://www.youtube.com/watch?feature=player_detailpage&v=zI...
NVidia: ~220 fps
Skia: 6 fps
Cairo: 29 fps
QT: 0.3 fps
Direct2D: 48 fps
(On a sidenote, I guess this is why IE10 is often faster at rendering SVGs than the other browsers: it uses Direct2D, whereas Firefox and Chrome use Skia).
Using graphics hardware should be more energy-efficient, so maybe we will finally see a change because of mobile devices with HD screens.
I think Firefox on Windows uses Direct2D, at least for Canvas, not sure if for img tags.
> (e.g. Ejecta computes all bezier points on the CPU and just draws on the GPU)
I guess that's where the biggest gain comes from with NVIDIA: it's the only pure-GPU option right now, which probably eliminates quite a few bottlenecks. Freeing up the CPU is also never a bad thing.
[1] http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html - https://github.com/hansent/lbfont is a good example
The Loop/Blinn work is really cool, and of course we now have hull shaders to really exploit it. It is a problem many people are actively working on...e.g. with Direct2D, but doing everything on the GPU is currently infeasible given round trip times between the CPU and GPU. Great if you can render everything in one pass, not so great if you can't.
Having said this, don't go turning this on everywhere, like on every tiny little UILabel. That GPU-accelerated CGContext is rendered in an offscreen pass using render-to-texture so it is often faster to use the CPU. This property was more designed for, say, a full-screen drawing app on a retina iPad that uses a a single screen-sized CGContext for its drawing canvas, or for the Safari case where there are a number of same-sized CGContext tiles to draw into.
http://www.youtube.com/watch?v=0IDyZof2pRI
(for those interested in the technical details of the NVIDIA thing, this is a lecture explaining it in detail).
EDIT: He explains in more detail in the first two minutes of part two:
I didn't know about this. I've been out of this game for a while. That looks great. I wish we'd had it back when I was working on a GPU XAML renderer.
Thanks!
If not, what is it?
http://www.slideshare.net/Mark_Kilgard/nvpathrendering-frequ...
(Q. 37)
This looks more sophisticated than the kind of tessellation I'm used to but they still have triangle fans!
GTK+ goes one better, and they compile an entire icon theme into a single mmap-able file with the data in a static GdkPixbuf format. Sadly I'm not sure how to map that to a QImage in general, otherwise I would have adapted that theme cache (where present) into KDE.
If you wanted to the same advantages with SVG, you shouldn't be using CSS sprites; you should take advantage of the fact that SVG is a text-based format and concatenate them all together. Then, you can use some really simple JavaScript to insert each child SVG into the correct place in the DOM.
The ideal way to handle this would look like this:
<img src = "icons.svg#map" />
where map.svg was the original file on the developer's machine. A grunt task could concatenate all the SVGs in a particular folder together and give them IDs according to their file names. In the client's browser, a JS prollyfill would iterate over all the img tags where src ends in a hash and replaces them with the child SVG of that ID.Just as the prevalence of $$ inspired querySelectorAll, an SVG-optimized spriting system could inspire browser vendors to properly handle hashes in src attributes. and the need for the prollyfill (or the ugliness of CSS spriting) would fade over time.
Sorry, I've had to write an HTTP compliant server recently, and I don't think there's any good way to send multiple files without completely redesigning the protocol, and even doing that, I don't think there's a good way to do it. Plus, keeping requests atomic makes more sense than bungling then together.
I know the main point of your comment wasn't too make HTTP better, just that sprites are, in your opinion, a bad hack, but your implying that HTTP is bad. Also, and this is just nitpicking, everyone uses 1.1, which has persistent connections in the spec, which help negate some if the computing cost for sending multiple files.
You can already use SPDY now (and fall back to HTTPS), FWIW.
I can't believe nobody else mentioned this.
Of note, a translate transformation still isn't perfect. It seems to chug and then turn into a smooth animation.
1. Animate #transform using jquery's animate (which doesn't work out of the box unfortunately, you have to use a custom #step fn)
2. Install "capture" event handlers on document.body that will cancel the animation, reset scrollTop to the state of the transform, then reset the #translate to 0 on #scroll or #touch (so we don't "fight" the user scrolling)
3. .. animation runs, calls my "complete" callback which uninstalls the event handlers, sets #translate back to 0, and updates scrollTop. Unfortunately there is a noticeable "glitch" here if the user tries to immediately further interact with the page.
#2 is optional (OP's implementation didn't handle canceling anyways). I'm sure there's a jquery plugin to do this and degrade to scrollTop on non-webkit browsers.
Edit: formatting.
For me, on Chrome Canary, the animation "chugs" every 20-30 frames (from 60 fps to a frame that takes 200ms). It's almost entirely in rendering, and it's immediately following a jQuery callback. It only happens when scrolling down. I'd like to see this done with a very simplistic requestAnimationFrame tweener to compare the same type of animation against something that is doing nothing else but computing a new scrollTop and setting it. I have a strong feeling, in Chrome at least, that the problem here is jQuery, not SVG perf.
Additionally, for other browsers, I'd like to see this tested with icon fonts.
On top of that, IE10 has no plugin model, so getting basics like privacy and ad blocking - there's just no reason to use IE10 outside of specific demos.
Changing a translate3d does not repaint the content, it simply translates a GPU texture (damn near free)
Sadly desktop browsers haven't caught up to their mobile equivalents yet and still have brain dead scrolling.
I would say PNG simply for the fact it seems to be a more accepted format than SVG.
Webkit acceleration is getting much better, and for those ancient IE browsers, check out RaphaelJs which translates them into MS approved VML.
This is the same type of issue as discussed in the example - it's a bug in the browser not a problem with SVGs in general.
See the related bug here: https://bugzilla.mozilla.org/show_bug.cgi?id=600207
SVG is nice because it gives us additional flexibility -- it's resolution independent (so you don't need to redraw your image by hand when higher-DPI displays come out) and allows for animation[0]. But if you don't need those things (and you probably don't for your sprites) then they'll just slow you down.
Browsers have not optimized SVG at all, painfully clear when the simplest retained dynamic SVG graphic is magnitudes slower than building entirely new framebuffers from scratch for every frame, ala canvas. It is trivial to see that the former should see dramatic optimizations that make it a significant winner, but in actual practice that just hasn't happened.
So it's a classic chicken/egg thing. No browser maker does anything to optimize SVG because few use it. Few use it because it's so unoptimized.
The findings of this post remain the same in either case, and is completely correct, but it shouldn't be this way. It isn't right that this test favors PNG.
It was my hope that retina screens would be the force to finally put real SVG support into browsers (and by real I mean both standards compliant and accelerated) because they benefit so much from line content using SVG.
I'm saying this from experience because I've actually been working on a library for supporting a subset of SVG in OpenGL because I wanted to use vector graphics for games, and it's not impossible or anything, but I will say I have a great deal of respect for projects that manage to get this fast. The SVG spec is not hardware friendly in the least bit. Almost every filter operation assumes a new clean render target, which is conceptually clean and fine, but it's murderous on fill rate and texture memory, and that's just the tip of the iceberg of things that translate poorly to modern hardware.
Optimizing all cases is most certainly hard. Optimizing the most common cases -- the lowest hanging fruit -- is attainable (which is exactly what has happened and continues to happen with dynamic typing languages, since you point that out, and has happened substantially to the DOM, which itself is as complex or more complex than SVG but its layout optimization was obviously prioritized).
In this case we're talking about static SVGs that -- with no change in zoom, context, or content (ergo nothing that would change their actual rendering at all) -- are completely rebuilt on every translation. This is true generally in the browser rendering engines of SVG, where compositional elements that could see caching are instead from scratch on each and every render.
So I recoded it to use canvas instead (with a switch to allow using one or the other) and there is just comparison, it's much faster everywhere. I've had reports that the SVG version was working fine on IE10 though, but couldn't test it because there's no IE10 for Linux of course.
It quickly becomes frustrating to make complex visualizations when SVG makes a lot of things much easier, but you constantly hit the limits of SVG performance on the common browsers.
You said something like that multiple times in your post, but IE seems to handle a lot of SVG work much better than Firefox or Chrome, and has done since at least version 9.
Flash supported this as a trade-off via the cacheAsBitmap attribute on elements. Basically you'd set that to true if you knew the sprite was just going to be translated around in x/y and not scaled or rotated. It wasn't on as the default for new sprites because this mode would actually bite you in the ass if the sprite was going to scale/rotate nearly each frame because the cost of recreating the "cached" version would then be added to the render time without actually ever being useful.
So it is a good idea in practical usage to make this an optional feature, though I think Flash could have been smarter about it (like soft-default to caching and only override that once it sees a scale or rotate taking place).
Again, this was probably 3-5 years ago, and older versions of Flash Player may have been less optimized. This may be one of the many optimizations they made to Flash Player after Apple decided it wasn't fast enough for iPhones.
The computationally complex bit is triangulating the image. If you can do that the graphics card will have an easier time drawing the vectors than it will shuffling all those textures around. In fact you could send all your vectors in one draw call.
What we need is browsers caching SVGs as geometry not pixels.
When scrolling the PNGs up and down, Safari oscillates between 64 to 94% CPU usage.
When scrolling the SVGs up and down, it goes between 36-54% CPU. Very very strange. I wonder if it has something to do with the fact that the SVG is dropping about a zillion frames.
I'm not sure there's much of a takeaway here unless you're trying to animate the page scrolling of a full page of sprites (i.e., the example proves itself, but not much else).
I followed the link which opens an html/css/js editor in which I cannot do anything. After some search I saw a white page with a list of icon a radio button switching from png to svg and some text about framerate where I do not see any animation or any difference between the two!
†: Saf' was advertised to do so, and since neither respect defaults write -g NSScrollViewRubberbanding -bool false while being butter smooth, I concur iTunes does so too.
Guys, please make your pages usable since (we), users, are not interested in you-think-it-is-cool effect. I'm going to avoid this page like plague until author fixes it.