I found several patterns for optimizing Bitmaps handling in WPF. I do not, however, understand when to use each patterns. As I think this is a common problem, I summarized what I understand and what I guess and ask for your help. If you can add patterns, explain how they differ, explain if they use the CPU or the GPU, and teach when to use each and how to combine them, it’d be a tremendous help!
Context – the Images "Grid" Scenario:
My application has to display many bitmap images. The images are displayed on the screen in a rows-and-columns grid-like organization (not necessarily the Grid or UniformGrid classes, think Window Media Player’s Album view). Images might move between different grid cells. Some images at arbitrary cells may be replaced by others. Images should be clickable, should provide a context menu, should be selectable, drag-able etc. In other words, “combine the little buggers into one big bitmap” is not applicable, at least not naively.
Pattern 0: The Hack
Do combine the little buggers into a bitmap (how? drawing context?), and use this as the background. Overlay this with images with empty content that will handle the hits, context menus, events etc.
The advantage is that we're only speaking about two bitmaps here: The currently displayed one and the one that should replace it. This should be really fast. However, my years of experience raise the red flag of danger. Your comments?
Pattern 1: Reduce Image Size
This is a no-brainer when you know in advance the image size to resize to, and when you’re prepared to lose details (color) for performance:
- Reduce the bitmap size using BitmapImage.DecodePixelWidth
- Reduce the color information using FormatConvertedBitmap.DestinationFormat
- Set the control’s scaling behavior setting Image.Stretch to Stretch.None
- Set the SetBitmapScalingMode for the image to LowQuality.
- Freeze the bugger
See code here.
Pattern 2: Background pre-fetch
This pattern is applicable when you think you can take advantage of the user gazing at the images on the screen, and prepare ahead the next images to display. The cons for your project, in addition to the memory overhead, is that it has to support the .Net Framework 4 target and not just the client profile, so it might incur an installation on the client’s. You yourself will have to suffer the async programming pain.
In this pattern you create exactly the required number of Image controls. When bitmaps need to be added, moved or deleted you only modify the Image controls' BitmapSource(s). A BackgroundWorker task is responsible for pre-fetching the BitmapSource(s) (possibly using the “Reduce Image Size” pattern above) and inserting them into MemoryCache.
For this to work you have to set the BitmapImage’s CacheOption to OnLoad, so that the work is off-loaded to the background worker.
Pattern 3: Drawing Context
This was suggested by Sheldon Ziao from Microsoft Support on the MSDN WPF forum here. See page 494, Chapter 15 “2D Graphics” in Adam Nathan’s WPF 4 Unleashed for a description of DrawingContext. I can’t say I understand it. According to the answer here, I would assume this would improve handling of Geometry drawings, not bitmaps. Next, I don’t think this will support the focus and events requirements for the images (my bad for not explaining the requirements better at the forum) Moreover, I’m worried by the book’s summary sentence: “Note that the use of DrawingContext doesn’t change the fact that you’re operating within a retained-mode system. The specified drawing doesn’t happen immediately; the commands are persisted by WPF until they are needed.” This means that once our even handler re we can’t take advantage of parallelism as in “Background pre-fetch”.
Pattern 4: Writeable Bitmaps
The MSDN documentation here describes it as a dual buffer system: Your UI thread updates the buffer; the WPF’s render thread moves this to video memory.
The intended usage (see here) is for bitmaps that change a lot such in a video movie like display. I’m not sure, but possible this could be hacked and combined with the Background Pre-fetch pattern and used in the grid scenario.
Pattern 5: Cached Bitmap
Not much info on the MSDN (here). On the WPF forum archive (here) it’s explained that “The BitmapCache API is designed to cache your content (when rendering in hardware) in video memory, meaning it stays resident on your GPU. This saves you the cost of re-rendering that content when drawing it to the screen.” This seems like a great idea. I’m not sure, however, what are the pitfalls and how to use it.
Pattern 6: RenderTargetBitmap
The RenderTargetBitmap converts a Visual to a bitmap. I’m not sure if it’s relevant here. See here.
Edit: Regarding Paul Hoenecke question: I've written that "My application has to display many bitmap iages". I failed to mentions that I need to display about 800 images concurrently.
One can read about the performance issues involved at my SO questions WPF Bitmap performance and How can I make displaying images on WPF more “snappy”?
I've modified the description of pattern 1 to highlight the concept that the image controls aren't created or deleted (unless we want to display a larger or smaller grid). Only their Sources are set to different, new or null BitmapSources.
Edit: This question as posted on the WPF support forum, with some answers from MS personnel.