Silverlight Performance Tip: Understanding the impact of Effects on performance

We recently received a request from a developer to help analyze performance for a basic Silverlight application. This particular app was used as one of the Silverlight SDK samples (See image below). The application had a simple set of controls on a form, but its CPU usage was very high. I wanted to share our findings and explain how we fixed the issue.

The first thing we did was to turn on EnableRedrawRegions. This lets you see which rectangles are being redrawn by the graphics engine within your application. Once enabled, all repainted regions’ colors will change in blue/yellow/pink order. This can be enabled by passing the parameter shown here in the application .aspx page:

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2">
<param name="enableRedrawRegions" value="true"/>
</object>

The issue:
Once we enabled redraw regions we could see that when we switched to a tab that contained an animation, the entire form was constantly being updated. Looking carefully at the form and also through the template sources we could see that the outer border (as well as one of the inner borders) was using a DropShadowEffect. This means that any redraw of any child element inside of that border’s visual tree will trigger a redraw of everything within the effect (in this case, the entire area of the border). So even with a small animation, the entire application was being redrawn every frame! Note that not only an animation will trigger a redraw, but any redraw such as cursor blink in a text box, or any redraw resulting from mouse movements over a control.

The solution:
We could have simply removed the DropShadowEffect, but we wanted to preserve the original design, so we did the following:

  1. Add a new sibling element the same size of the border that has the DropShadowEffect, but contains no child elements.
  2. Removed the Effect from the original border.

Below simple changes provided huge performance improvements.
You are also welcome to dowanload attached EffectSample.zip code sample.

Original XAML:
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Border Background="{StaticResource WindowBackgroundBrush}" Effect="{StaticResource DropShadowBrush}">
     <Grid MinWidth="150">
...
         <ProgressBar Grid.Row="1" IsIndeterminate="True" Height="8" Margin="5"/> <!-- Animating progress bar >
     </Grid>
  </Border>
</Grid>

Fixed XAML:
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
  <!-- Keep this border (which has DropShadowEffect), make sure it has no children -->
  <Border Background="{StaticResource WindowBackgroundBrush}" Effect="{StaticResource DropShadowBrush}" />
  <!-- Create a new border sibling border w/o any effects -->
  <Border>
<Grid MinWidth="150">

<ProgressBar Grid.Row="1" IsIndeterminate="True" Height="8" Margin="5"/> <!--Animating progress bar >
</Grid>
</Border>
</Grid>

 Summary:

  1. Enable RedrawRegion to see what areas of your application are updating.
  2. If you must use Effects make sure they are applied to small areas. Otherwise,
  3. Make sure that Elements that use effects contain no child elements that could update (e.g. even Textbox with a blinking cursor or mouse hover over a button can impact perf,). Otherwise,
  4. Remove the effect from that element and create new empty sibling element (which has no children) and apply effect on this new element instead.

   

 

Enjoy. Jossef

EffectSample.zip