Friday, October 16, 2009

Performance degrades in your Silverlight App!? What can you do?


Some time ago I had written Silverlight app and I confronted with difficulties of difference between WPF and Silverlight :). When I had vanquished mismatches I found that performance of my app was serous degraded. I have done a little investigate of ways that can help to correct this issues.
  1. MaxFrameRate
  2. EnableGPUAcceleration & BitmapCash
  3. CompositionTarget.Rendering
MaxFrameRate - presents the maximum number of frames that Silverlight can render per second. The default value is 60 second. I've set 30 in my app - looks pretty. This property likes DesiredFrameRate in WPF. Target is clear - reduce frame rate. It is useful when more animations are started in Silverlight app.
You can set it from Silverlight code:

App.Current.Host.Settings.MaxFrameRate=30;

or as parameter of object on HTML page:

<param name="framerate" value="30">

Pay attention there is a mismatch naming in Silverilght code and HTML. Why is it? You can read there.

EnableGPUAcceleration & BitmapCash - allows to cache visual object when it is possible. You should switch on EnableGPUAcceleration that Cache has any effect. This approach is useful when your app has opacity or transform operations applied to it.
You can set it from Silverlight code:

You can't change it from Silverlight because it should be set before app's initialization.

or as parameter of object on HTML page:

<param name="EnableGPUAcceleration" value="true" />

and set CacheMode. In current moment BitmapCache is supported only.
You can set it in C# code:

LayoutRoot.CacheMode = new BitmapCache();

or in XAML:

<Grid x:Name="LayoutRoot" CacheMode="BitmapCache">

Some objects don't look well (high-resolution images for example) you can increase RenderAtScale property of BitmapCache class for avoid this mess. Default value of this property is 1, when I set 4 (like MSDN's samples) quality restores but performance degrades again. A value between 2-3 is god for my app.
You can set it in C# code:

LayoutRoot.CacheMode = new BitmapCache
{
RenderAtScale = 2.5,
};


or in XAML:

<Grid x:Name="LayoutRoot">
<Grid.CacheMode>
<BitmapCache RenderAtScale="2.5" />
</Grid.CacheMode>
</Grid>


CompositionTarget.Rendering - this event rises before the objects in the composition tree are rendered. How does it can help? In common case - doesn't. But if you zoom your object in app (or in same other manner move out of bounds some item) - it is very useful. Look in my case:
First - subscribe event handler's method in constructor of your main window:

CompositionTarget.Rendering += new EventHandler( OnCompositionTargetRendering );

Second - handler method:

private void OnCompositionTargetRendering( object sender, EventArgs e )
{
//--- Start 1 -- //
foreach ( GalleryItem item in xGalleryCanvas.Children )
{
if ( Visibility.Collapsed == item.Visibility )
{
item.Visibility = Visibility.Visible;
}
}
//--- End 1 -- //
//--- Start 2 -- //
if ( _isZoomed && 0 < rect =" new" visibleitems =" VisualTreeHelper.FindElementsInHostCoordinates(" f =""> f.GetType() == typeof( GalleryItem ) );

foreach ( GalleryItem item in xGalleryCanvas.Children )
{
if ( !visibleItems.Contains( item ) )
{
item.Visibility = Visibility.Collapsed;
}
}
//--- End 2 -- //
}

Some explanation: You should ascertain that all items are visible (block 1).
Note: Dependency property works not so good as in WPF. So you should change Visibility only of collapsed items. In other case you serious reduce the performance.
Use mix of FindElementsInHostCoordinates() of VisualTreeHelper and LINQ for create collection all visible items in current moment and collapse all other items (block 2).

Promise these three approaches can help as these help me.

PS: Recently I've found useful tips of Silverlight performance. You can read it there - Performance Tips.

No comments: