9

I’m writing a WPF image viewer, displaying a grid of images. I’m baffled because of the sluggish performance: Displaying even an 11 x 11 grid makes a VM unresponsive, slow and sluggish for long durations of times. Even on the powerful host the performance in non-snappy.

The program is based on the design in SO WPF: arranging collection items in a grid: An ItemsControl is bound to Items, an ObservableCollection. Each Item contains has a file-system absolute URI. The ItemsControl‘s DataTemplate contains an Image element whose Source is bound to the URI.

It seems the problem can’t be the disk (SSD), the memory (8GB VM, 24GB host), or CPU (i750). Moreover, most of the work is done by WPF, so it’s not even as if I could locate a problem in my code: My code simply loads URIs (i.e. the paths to the images, not the images) to the collection and quickly returns. Then there is a wait, and WPF displays the images.

The only problem I could think about is image processing – down scaling by WPF. But even on the host, which has a "good-enough" 5850 ATI Radeon HD card, the performance is not snappy.

So, my question is: How can I make displaying images on WPF more “snappy”?

Edit: The images are 1920x1080 22 bit HD JPEGs captured from HD m2ts video. I tried pre-scaling them (using FFmpeg) to 'ega' 640x350. There was a performance improvement but FFmpeg's scaled-down images looks much worse than WPF's.

Edit: Thanks to David Osborne the code now runs as x64. Still sluggish.

Edit What really improved the situation is what Matěj Zábský called scalling the images: reducing the resolution. For the benefit of future readers:

            fullPath = new Uri(path, UriKind.Absolute);


            BitmapImage smallerBitmapImage = new BitmapImage();
            smallerBitmapImage.BeginInit();
            smallerBitmapImage.DecodePixelWidth = (int) (theWidthOfTheGrid / theNumberOfColumns);
            smallerBitmapImage.UriSource = fullPath;
            smallerBitmapImage.EndInit();

            FormatConvertedBitmap formatConvertedBitmap = new FormatConvertedBitmap();
            formatConvertedBitmap.BeginInit();
            formatConvertedBitmap.Source = smallerBitmapImage;
            formatConvertedBitmap.DestinationFormat = PixelFormats.Gray16;
            formatConvertedBitmap.EndInit();
            formatConvertedBitmap.Freeze();

            this.ImageSource = formatConvertedBitmap;
Community
  • 1
  • 1
Avi
  • 15,696
  • 9
  • 39
  • 54

4 Answers4

9

I have been in similar position (I had to display thumbnail versions of large images in real-time).

If you are using data binding to display the images, you can try two things:

  • Make the binding OneWay (this helped me the most).
  • Try making the binding async.

The only other thing is to prescale the images - preferably separate thread. Possibly you could even prescale images which could be potentially used in the future while the processor is idle - for example if you are developing a map-like application, you could prescale images in areas to which the user is most likely to move.

Matěj Zábský
  • 16,909
  • 15
  • 69
  • 114
  • Thanks. Setting all the bindings in the XAML to "{Binding ..., Mode=OneWay} appears to have improved the host performance from 8 to 6 seconds. Setting the ItemsControl collection binding to async may have reduced the delay to 5 seconds. That's great, but we're not yet there ... – Avi Jan 22 '12 at 11:16
  • @avi You could also try playing with DecodePixelWidth http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.decodepixelwidth%28v=VS.100%29.aspx I'm not sure if it works with binding though. – Matěj Zábský Jan 22 '12 at 11:29
  • Is this what you meant with "prescale"? – Avi Jan 22 '12 at 11:44
  • @avi Not really. I meant that there would be a routine somewhere in the code which would run in separate thread (possible even in more than one thread at once) and then only downscaled images would be fed into WPF. Of course multi-threading in WPF is not entirely trivial (though IIRC bindings can handle it - I think you can bind to an object which is owned by another thread). – Matěj Zábský Jan 22 '12 at 11:52
  • @Matej But what would the bacgrkound task use to downscale the images (if not DecodePixelWidth)? – Avi Jan 22 '12 at 12:00
  • @Avi I guess it could use DecodePixelWidth. I never worked with DecodePixelWidth myself, so I can't confirm how useful it is (my application generates the displayed images on the fly, so it is of no use to me - I just wrote my own routine to scale the original data array from which I generate the images). This could be also of interest to you: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/580fca03-d15b-48b2-9fb3-2e6fec8a2c68/ – Matěj Zábský Jan 22 '12 at 12:04
2

You can try Virtualization for control, It help considerably when loading more images in listview. VirtualizingStackPanel

<ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel Orientation="Vertical" VirtualizingStackPanel.IsVirtualizing="True"  VirtualizingStackPanel.VirtualizationMode="Recycling"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>

If want to use Viruatalization with wrap-panel, there is some from Codeplex, Virtualalizing Wrappanel

I know it is an old thread, but if someone is still interested.

Hardik
  • 1,716
  • 6
  • 27
  • 41
1

If you're running on a VM, you are probably hitting issues with hardware (or at least emulated-hardware) rendering of graphics. WPF makes heavy use of hardware graphics and, on machines with low graphics capability, performance will suffer.

You can work around this by enabling a registry setting that forces-off the hardware acceleration. This is particularly useful on machines that may have over-reported their capability (from experience, this particularly affects VMs and embedded graphics chipsets).

The registry key you need is HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\DisableHWAcceleration, a DWORD that you should set to 1. Note that this is system-wide and will affect all WPF apps - don't set it as part of your application, but use it in your VM environment and make sure you test again on proper hardware before releasing.

Further reading here.

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
1

Have you tried experimenting with different Target Platform settings? I.e. x86 or AnyCPU.

David Osborne
  • 6,436
  • 1
  • 21
  • 35
  • Thank you. This was indeed faulty. Now I'm running in x64. However, host (i.e i750, 24G RAM, SSD, 5850 HD) still takes 8 seconds to advance to the next 11x11 view. – Avi Jan 22 '12 at 10:23