Having spent a couple of months focused on Windows Phone 7 development, I must admit that Microsoft have managed to bring something completely unique and intriguing to the mobile world. Not only the fact that the UX has nothing to do with what’s currently on the shelves, but also the whole platform with its potential and developer accessibility make WP7 very thrilling. I really think that ‘Silverlight’ and ‘mobile’ are two words that should have been stick together anytime soon and with WP7 Microsoft made what should bring them back on the scene – they simply brought Silverlight into mobile.
In general, developing for Windows Phone 7 should be really straightforward since Silverlight is a known platform; the tools are the same etc. Interestingly, however, there are not two or three things that you should be aware of in order to make your WP7 app rock in terms of performance and memory footprint. My focus here will be put particularly on graphics and how the CPU and the GPU on a typical Windows Phone 7 device work together. These are thoughts based on my experiments that I would like to share and thus keep the knowledge base fresh and active. I have also prepared a small WP7 application that you can use to experiment and explore the topic further.
As you know, Silverlight for WP7 utilizes two main threads to handle user interactions and render tasks – the UI thread (taking care of initial rendering, user input, callbacks etc.) and the Compositor thread (ensuring the GPU acceleration). Both threads work together and knowing how to handle them is important when the aim is to integrate graphics and good performance into your applications.
The initial rendering of an element happens on the UI thread. The Compositor thread, on the other side, works with already rendered elements which are cached in the memory as bitmaps. When a property change occurs on a cached element that implies re-rendering of the element (for instance changing a color, applying non-rectangular opacity mask, changing border thickness, setting padding/margin etc.) the corresponding bitmap stored in the phone’s memory will be erased, the element will be rendered on the UI thread and a new bitmap will be cached.
The BitmapCache brings significant performance benefits in terms of rendering since the render phase is skipped for cached elements. Also some of the render operations that can be performed on an element are GPU accelerated when the corresponding element is cached. In other words, if you apply, let’s say, ScaleTransform to a cached element, the UI thread will hand out the cached bitmap of the element to the GPU to do the math.
The Compositor thread is a very lightweight thread that is synchronized with the UI thread and operates mainly with cached bitmaps and the GPU. It cannot be utilized directly from the UI thread, i.e. it is hidden from the developer and is used internally by the WP7 OS when a given type of animations is performed on cached elements.
For instance, if you create a Storyboard with a DoubleAnimation in it that animates a ScaleTransform object on an element, the element will be automatically cached and the animation will be run on the Compositor thread. Another approach for the same case is to use a timer and to apply a ScaleTransform on the element in its Tick handler. Things look a bit different here: the element, if not explicitly cached, will be re-rendered each time on the UI thread. If you explicitly set a BitmapCache to the element, a bitmap of it will be created and the transform operations will be provided by the GPU. The animation, however, will still be executed on the UI thread as the Timer callback is performed there.
A good approach to understand how these two threads cooperate is the empiric one. For the purpose of demonstrating what I have previously discussed, I have prepared a very simple Windows Phone 7 application that implements a couple of interesting scenarios to give better explanations.
This sample application performs a ScaleTransform animation on two elements using the approaches described above:
• Storyboard-based animation
• Timer-based animation
Use the 'Block UI Thread' button to sleep the UI thread for 5 seconds; use the 'Toggle Cache' button to change the CacheMode of the UI Thread animated element.
You can use the “Block UI Thread” button to make the UI thread sleep for 5 seconds and observe the behavior of the animated elements – the Storyboard animation continues while the Timer-based animation halts. You can also play with toggling the BitmapCache property of the Timer-animated element and the Application.Current.Host.Settings.EnableRedrawRegions setting to see how it behaves on the phone. When no BitmapCache is set, the timer-animated element is constantly redrawn, otherwise it is cached in the phone’s memory and the ScaleTransform is performed by the GPU without a render pass on the UI thread. It is also worth noting, that even though driven by a Storyboard, a ScaleTransform animation causes re-render pass for the element if the scale amount exceeds 50% of the original element size.
You can download the application’s source by using the following link: