originally posted by Evan Lang: (link) - please comment at original post
The WPF team, by design, hid what they could of the underlying mechanisms used to render graphical elements in WPF. The overall philosophy was to make WPF a designer-centric universe, where users care more about things being more or less intuitive and “just working” than the more coder-esque ideals of ensuring things work optimally, are sufficiently generalized, etc. It does rather well. By and large, you can produce a complex, and even good-looking, UI in WPF without thinking too hard about things like performance and compatibility. VS 2010 is being developed in WPF, is certainly one of the most complex form-based UIs around, and WPF does just fine with it.
My job, however, is to push the boundaries of WPF beyond what it might normally be able to do, and I’ve become quite familiar with its performance limitations. Since the WPF team offers very little performance-based configurability in the API, I’ve simply had to learn generally how much XYZ element will cost, and I’ve been surprised on several occasions, so I thought I’d share some.
- WPF’s graphics rendering tiers - These don’t “perform” technically, but they’re closely related and utterly useless. I might be exaggerating, but I’d guess that $199 netbooks could achieve the highest tier without a problem. That doesn’t really provide me much information.
- VisualBrush - Microsoft correctly identifies this monster as the slowest brush around. If you are lucky enough to not have to animate your VisualBrush, you can cache it using RenderOptions. If it animates at all, however, I’ve found its performance cost to be unbelievably massive. I’ve used them before to pass an animating, 192×128 (tiny) D3DImage into a ShaderEffect (which only accepts VisualBrush and ImageBrush as texture parameters), and found that 75-80% of the machine’s time was spent working on the VisualBrush. Replacing it with an ImageBrush helped tremendously, but then I lost linear interpolation in my image (due to what appears to be a bug in WPF’s ShaderEffect implementation) and the quality went to hell. I can’t think of any reason VisualBrush should perform as poorly as it does, so if at all possible, I’d avoid using it unless you can cache it.
- ImageBrush - I must say ImageBrush isn’t incredibly expensive, overall, but I have noticed issues with it on slower machines or machines with not-so-much RAM. Fundamentally, what ImageBrush does is actually one of the very first capabilities that the very first commercially sold graphics accelerators could do. The techniques of tiling, stretching, and linearly interpolating an image are ancient and on video cards today are basically free, in terms of performance. Unfortunately, it seems like WPF shoots itself in the foot here, as every time I’ve worked with it, I get the impression that WPF first renders the tiling to an intermediate buffer. So something that should have no cost, in fact can be quite costly on a system with limited RAM.
- Vector Art - WPF does a damn fine job of rendering vector art… not quite at the level Flash is at yet, but damn fine. As a WPF user, however, you should understand that rendering vector art is actually a fairly complex operation, and if you use and animate a lot of it, it will add up. Most of the time, vector art is static… you might move it around, rotate it, or rescale it, but the shapes are usually the same. In this case, it’s a good idea to consider rendering the vector art to a RenderTargetBitmap and using it instead. IdentityMine has a helpful control that does this (mostly) automagically in their Blendables control suite, called ElementSnapshot. Or, if you’d prefer to wait, WPF 4.0 will supposedly allow you to explicitly cache elements, making such controls obsolete.
- BlurEffect/DropShadowEffect - These are the two original BitmapEffects, that were (supposedly) revamped to be hardware accelerated when .Net 3.5 SP1 was released. If that’s the case, they don’t behave that way. Both of these effects perform much worse than I would expect. In addition to having a large overhead cost, they both seem to exhibit a very strong O(n^2) relationship with respect to the blur size. The fact that the number of pixels to render is also O(n^2) means that theoretically you can’t do much better, but just rendering pixels is fairly cheap, up to a certain size (on most machines around 2k x 2k, subjectively). In-house we have a re-engineered BlurEffect that doesn’t seem to have the same initial overhead, runs orders of magnitude faster, and always renders the “Quality” version of the image (as opposed to BlurEffect’s “Performance” version). In fact, it includes a property allowing you to bound the blur within the original boundaries of the image (so no additional pixels get rendered), in which case the blur op is O(log(n)) with respect to the blur size. Point being, these effects weren’t very well thought out, hardware accelerated or not, so if you want to get good use out of them, you may want to think about just using ShaderEffects to rewrite them.
- Gradient brushes - Like ImageBrush, it seems that these get rendered to an intermediate buffer before being applied to the final image. With the advent of ShaderEffects, this is generally an unnecessary waste of system time and resources. They should be extremely cheap, but I’ve found that I do need to be rather careful how much I utilize them. Unfortunately there aren’t many workarounds for these, particularly if they are animated, so just consider this a gripe to the WPF team.
- MediaElement - I wanted to add this to the list because you’d think there’d be some odd performance difficulties surrounding videos, but frankly, there really aren’t. I’ve been very surprised overall at how well they perform… not so impressed with other aspects of it, but they do generally run very smoothly. I’ve had a 8 simultaneous WMVs running on a 2.5-year-old machine before without any problems at all. I’ve also applied several ShaderEffects to a 1080p HD-WMV on the same machine, again with no hiccups.
Most everything else I think performs as well as you would expect it to. When WPF 4.0 comes out it’ll be important to know when to use composition caching to minimize the amount of rendering work the system does. In the meantime using IdentityMine’s ElementSnapshot works pretty well, too.
Remember to please comment at original post: (link)