• Improving WPF Rendering performance (Part 2)

    by  • October 12, 2012 • .Net, Journal, Programming • 0 Comments

    This article is part of a series of articles I have written covering WPF; improving its performance, its quirks and associated workarounds, as well as porting it to WUA Applications:

     

    You’ve created a beautiful looking WPF application, but realise it runs horrendously and uses too much memory? Well here are some more tips and tricks that I have learnt along the way.

    There are lots of tips and tricks when it comes to ‘optimising’ WPF. I put optimising in quotations as it could be classed as slightly ironic. Some of these tips and tricks help reduce CPU/GPU/Memory load however they can come at a cost, whether it be reduced frame rates or slight degradation in image quality. I will guide you through most of them and you can make up your own mind.

    WPF Animation Framerate

    The WPF animation loop has been designed by default to run at 60FPS, this means that the render loop can run hot while nothing much is on screen. If you are porting from Flash especially (where framerates are usually set at 30 anyway) it may be a good idea to adjust this value. The improvement is best seen when nothing much is happening on screen, or if the change between frames is small.

    Timeline.DesiredFrameRateProperty.OverrideMetadata(
        typeof(Timeline),
        new FrameworkPropertyMetadata { DefaultValue = 30 }
    );
    

    This can in extreme cases nearly half the CPU usage. Try adjusting the DefaultValue to even lower values for idle screens or when no animations are occurring.

    Render Options

    So once everything is using the hardware there still can be slowdowns and stutters caused elsewhere in your program, one of the big issues I’ve had lately is how WPF handles ImageBrush, when loading an image it will load the entire image which if your rendering a 10mp digital photo can cause a huge slowdown if you’re using multiple instances of different brushes. There are a few tricks to start off with, firstly by altering the RenderOptions of the ImageBrush to increase its performance. Below is a sample of code that alters the RenderOptions of the brush _PictureBrush.

    RenderOptions.SetCachingHint(_PictureBrush, CachingHint.Cache);
    RenderOptions.SetBitmapScalingMode(_PictureBrush, BitmapScalingMode.LowQuality);
    

    Bitmap Caching

    An area of slow down is when complex objects have to be rendered repeatedly to the screen, what makes it worse is if these never change and are static. The CLR and compiler can notice this to varying degrees and can perform optimisations, but if you know when something won’t change for a long time; a button, or control, it might be best to give the CLR the heads up.

    Bitmap caching works by rendering the control once, and then storing the finalised bitmap version. Future renders will use this cached version rather than needing to redraw the entire control again, this can lead to substantial improvements.

    Caching has problems with the following:

    • Animations are happening within the control
    • Video
    • Interaction with native controls
    • Transformations (can cause image corrupts / degradation)

    Reducing Image Footprint
    Adjusting caching options to reduce the image quality may not be enough as the pictures are still being loaded into memory at their original size, therefore to reduce their footprint, what we want instead to do is to produce and use instead a thumbnail of the original image.

    public BitmapImage CreateThumbnail(Uri Source, int PreferredWidth)
    {
        BitmapImage bi = new BitmapImage();
        bi.BeginInit();
        bi.DecodePixelWidth = PreferredWidth;
        bi.CacheOption = BitmapCacheOption.OnLoad;
        bi.UriSource = Source;
        bi.EndInit();
        return bi;
    }
    

    This code takes a Uri Source and creates a thumbnail of the PeferredWidth, thus if you use this method you can drastically reduce the memory requirements of handling images as Brushes.

    WPF Performance Suite

    A little known tool included within the Windows SDK is the WPF Performance Suite, a set of tools that lets you drill down and analyse the runtime behaviour of a WPF application to see where there are bottlenecks, and most importantly I find; to see what parts are hardware vs software rendered.

    The two tools initially exposed by the Performance Suite are the Perforator and Visual Profiler. The Perforator allows the real time monitoring of performance data including framerate, Dirty Rects, and importantly IRTs (Intermediate Render Targets) IRTs are a big no no, and using software IRTs clearly shows that some of the graphics work is not being pushed over to the GPU.
    
    The Visual Profiler allows the drilling down into the XAML elements to uncover hot paths and areas which are not fine tuned.


    Font Caching

    WPF has a font caching service which loads with Windows (WPF Font Cache) which can be turned on and off via Services.msc.

    From MSDN:

    “The WPF Font Cache service shares font data between WPF applications. The first WPF application you run starts this service if the service is not already running. If you are using Windows Vista, you can set the “Windows Presentation Foundation (WPF) Font Cache 3.0.0.0” service from “Manual” (the default) to “Automatic (Delayed Start)” to reduce the initial start-up time of WPF applications”

    Sometimes the Font Cache service can crash, or load in an inconsistent state, to fix this, you could delete the Font Cache on PC start:

    Delete all of the WPFFontCache_v0400* files. In Windows XP, you’ll find them in your C:\Documents and Settings\LocalService\Local Settings\Application Data\ folder

    Reducing the number of fonts in the system will improve the performance of the Font Caching service and reduce the overall cache size, although this is a small process and really shouldn’t need to be touched.
    Freeze Assets

    Certain assets can be frozen to improve performance, this is where the asset is changed to read only and can not be modified, this allows the CLR to stop constant dependency checks to determine if field values have changed. This can greatly increase the performance of brushes and effects if they are reused multiple times.

    if (brush.CanFreeze)
    {
        brush.Freeze();
    }
    

    Making resources static

    When producing a complex layout in XAML, you may be using the same brush, or path repeatedly. If that is the case then it may be wise to place the resource once in the windows resources attribute, and then reference it as a static resource throughout your XAML.

    
    
    
      F1M0,0L10,10
    

    Above shows how the example can be applied to a Path, where the contained StreamGeometry is stored in the Window Resources. Each resource is then stored with a corresponding key which is used to access it as a Static Resource via any element in the Window with the correct type.

    About

    Software engineer. Tea drinker

    http://MrPfister.com

    Leave a Reply

    Your email address will not be published. Required fields are marked *