Logo
programming4us
programming4us
programming4us
programming4us
Home
programming4us
XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
Windows Phone

Animations : A Cautionary Tale

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
4/3/2011 11:21:16 AM
I’ve showed you how to use CompositionTarget.Rendering for moving and changing visual objects in synchronization with the refresh rate of the video display. While certainly convenient for some scenarios, this feature of Silverlight should be used with discretion. If you’re really interested in using CompositionTarget.Rendering for a full-fledged game loop, for example, perhaps it’s time to start thinking about XNA.

The big problem is that sometimes CompositionTarget.Rendering does not work as well as you might anticipate. Here is a program that attempts to use CompositionTarget.Rendering to rotate that spiral.

Example 1. Silverlight Project: RotatedSpiral File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage
{
RotateTransform rotateTransform = new RotateTransform();

public MainPage()
{
InitializeComponent();
Loaded += OnLoaded;
}

void OnLoaded(object sender, RoutedEventArgs args)
{
Point center = new Point(ContentPanel.ActualWidth / 2 - 1,
ContentPanel.ActualHeight / 2 - 1);
double radius = Math.Min(center.X, center.Y);

Polyline polyline = new Polyline();
polyline.Stroke = this.Resources["PhoneForegroundBrush"] as Brush;
polyline.StrokeThickness = 3;

for (double angle = 0; angle < 3600; angle += 0.25)
{
double scaledRadius = radius * angle / 3600;
double radians = Math.PI * angle / 180;
double x = center.X + scaledRadius * Math.Cos(radians);
double y = center.Y + scaledRadius * Math.Sin(radians);
polyline.Points.Add(new Point(x, y));
}
ContentPanel.Children.Add(polyline);

rotateTransform.CenterX = center.X;
rotateTransform.CenterY = center.Y;
polyline.RenderTransform = rotateTransform;

CompositionTarget.Rendering += OnCompositionTargetRendering;
}

void OnCompositionTargetRendering(object sender, EventArgs args)
{
TimeSpan elapsedTime = (args as RenderingEventArgs).RenderingTime;
rotateTransform.Angle = 360 * elapsedTime.TotalSeconds / 3 % 360;
}
}


Most of this code is the same as the Spiral program, but notice the RotateTransform field. At the end of the Loaded handler, this RotateTransform is set to the RenderTransform property of the Polyline defining the spiral, and a handler is attached to the CompositionTarget.Rendering event. That event handler changes the Angle property of the RotateTransform to rotate the spiral once every 3 seconds.

There’s nothing really wrong with this code, but if your phone is similar to my phone, the performance will be terrible. The screen will be updated only once or twice a second, and the resultant animation will seem very jumpy.

I’m going to tell you three ways to fix the problem and improve the performance. Fortunately, all three ways to fix the problem are general enough to be usable beyond this particular application.

Solution 1: Simplify the graphics. This spiral is a Polyline with 14,400 points. That is way more than sufficient. If you change the increment in the for loop from 0.25 to 5, the animation will be much smoother and the spiral itself will still seem round. The lesson: Fewer visual objects often result in better performance. Simplify your graphics and simplify your visual trees.

Solution 2: Cache the visuals. Silverlight is attempting to rotate a Polyline with very many individual points. It would find this job a lot easier if the spiral were a simple bitmap rather than a complex Polyline. You could make a WriteableBitmap of this graphic yourself and rotate that. Or you could let Silverlight do the equivalent optimization by simply setting the following property on Polyline:

polyline.CacheMode = new BitmapCache();

This instructs Silverlight to create a bitmap of the element and to use that bitmap for rendering. You shouldn’t use this option for vector graphics that dynamically change. But complex graphics that are static within themselves, and which might be subjected to animations, are excellent candidates for bitmap caching. In XAML it looks like this:

CacheMode="BitmapCache"

Solution 3: Use Silverlight animations instead of CompositionTarget.Rendering.

Let’s rewrite the RotatedSpiral program with the same number of points in the Polyline and without explicit bitmap caching but replacing CompositionTarget.Rendering with a DoubleAnimation:

Example 2. Silverlight Project: AnimatedSpiral File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
Loaded += OnLoaded;
}

void OnLoaded(object sender, RoutedEventArgs args)
{
Point center = new Point(ContentPanel.ActualWidth / 2 - 1,
ContentPanel.ActualHeight / 2 - 1);
double radius = Math.Min(center.X, center.Y);

Polyline polyline = new Polyline();
polyline.Stroke = this.Resources["PhoneForegroundBrush"] as Brush;
polyline.StrokeThickness = 3;

for (double angle = 0; angle < 3600; angle += 0.25)
{
double scaledRadius = radius * angle / 3600;
double radians = Math.PI * angle / 180;
double x = center.X + scaledRadius * Math.Cos(radians);
double y = center.Y + scaledRadius * Math.Sin(radians);
polyline.Points.Add(new Point(x, y));
}
ContentPanel.Children.Add(polyline);

RotateTransform rotateTransform = new RotateTransform();
rotateTransform.CenterX = center.X;
rotateTransform.CenterY = center.Y;
polyline.RenderTransform = rotateTransform;

DoubleAnimation anima = new DoubleAnimation
{
From = 0,
To = 360,
Duration = new Duration(TimeSpan.FromSeconds(3)),
RepeatBehavior = RepeatBehavior.Forever
};

Storyboard.SetTarget(anima, rotateTransform);
Storyboard.SetTargetProperty(anima,
new PropertyPath(RotateTransform.AngleProperty));

Storyboard storyboard = new Storyboard();
storyboard.Children.Add(anima);
storyboard.Begin();
}
}


And it runs much smoother than the previous version.

Why the big difference? Surely on some level the Silverlight animations are making use of something equivalent to CompositionTarget.Rendering, right?

Actually, that’s not true. It’s pretty much true for the desktop version of Silverlight, but Silverlight for Windows Phone has been enhanced to make greater use of the phone’s graphics processing unit (GPU). Although GPUs are customarily associated with hardware accelerations of complex texture processing and other algorithms associated with 3D graphics, Silverlight puts the GPU to work performing simple 2D animations.

Much of a Silverlight application runs in a single thread called the UI thread. The UI thread handles touch input, layout, and the CompositionTarget.Rendering event. Some worker threads are also used for jobs such as rasterization, media decoding, sensors, and asynchronous web access.

Silverlight for Windows Phone also supports a compositor or render thread that involves the GPU. This render thread is used for several types of animations of properties of type double, specifically:

  • Transforms you set to the RenderTransform property

  • Perspective transforms you set to Projection property

  • Canvas.Left and Canvas.Top attached properties

  • Opacity property

  • Anything that causes rectangular clipping to occur

Animations that target properties of type Color or Point continue to be performed in the UI thread. Non-rectangular clipping or use of OpacityMask are also performed in the UI thread and can result in poor performance.

As a little demonstration, the UIThreadVsRenderThread project rotates some text in two different ways:

Example 3. Silverlight Project: UIThreadVsRenderThread File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<TextBlock Grid.Row="0"
Text="UI Thread"
FontSize="{StaticResource PhoneFontSizeLarge}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5 0.5">
<TextBlock.RenderTransform>
<RotateTransform x:Name="rotate1" />
</TextBlock.RenderTransform>
</TextBlock>

<TextBlock Grid.Row="1"
Text="Render Thread"
FontSize="{StaticResource PhoneFontSizeLarge}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5 0.5">
<TextBlock.RenderTransform>
<RotateTransform x:Name="rotate2" />
</TextBlock.RenderTransform>
</TextBlock>

<Button Grid.Row="2"
Content="Hang for 5 seconds"
HorizontalAlignment="Center"
Click="OnButtonClick" />
</Grid>


The first TextBlock is rotated in code using CompositionTarget.Rendering. The second TextBlock is animated by the following Storyboard defined in the page’s Resources collection:

Example 4. Silverlight Project: UIThreadVsRenderThread File: MainPage.xaml (excerpt)
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="storyboard">
<DoubleAnimation Storyboard.TargetName="rotate2"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:1:0"
RepeatBehavior="Forever" />
</Storyboard>
</phone:PhoneApplicationPage.Resources>

The MainPage constructor starts the animation going and attaches a handler for the CompositionTarget.Rendering event.

Example 5. Silverlight Project: UIThreadVsRenderThread File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage
{
DateTime startTime;

public MainPage()
{
InitializeComponent();

storyboard.Begin();
startTime = DateTime.Now;
CompositionTarget.Rendering += OnCompositionTargetRendering;
}

void OnCompositionTargetRendering(object sender, EventArgs args)
{
TimeSpan elapsedTime = DateTime.Now - startTime;
rotate1.Angle = (elapsedTime.TotalMinutes * 360) % 360;
}

void OnButtonClick(object sender, RoutedEventArgs args)
{
Thread.Sleep(5000);
}
}

I’m using the time-based logic for CompositionTarget.Rendering so the two animations move at the same rate. But press that button and you’ll see something amazing: The TextBlock rotated by CompositionTarget.Rendering stops dead for five seconds, but the one powered by DoubleAnimation keeps right on going! That’s the render thread working for you even though the UI thread is blocked.

If you’re applying rotation and scaling to text, there’s another setting you might want to know about. This is the attached property you set in XAML like this:

TextOptions.TextHintingMode="Animated"

The alternative is Fixed, which is the default. When you indicate that text is to be animated, certain optimizations for readability won’t be performed.

Silverlight has three built-in features that can help you visualize performance issues. Although you can use these on the phone emulator, the emulator tends to run faster than the actual phone, so you’re not getting a true view of performance anyway.

For more details about performance issues, you’ll want to study the document “Creating High Performance Silverlight Applications for Windows Phone” available online The Settings class in the System.Windows.Interop namespace has three Boolean properties that can help you visualize performance. You access these three properties through the SilverlightHost object available as the Host property of the current Application object. These properties are considered so important that you’ll find them set—and all but one commented out—in the constructor of the App class in the standard App.xaml.cs file.

Here’s the first:

Application.Current.Host.Settings.EnableFrameRateCounter = true;

This flag enables a little display at the side of the phone showing several items:

  • The frame rate (in frames per second) of the render thread (GPU)

  • The frame rate (in frames per second) of the UI thread (CPU)

  • The amount of video RAM in use in kilobytes

  • The number of textures stored on the GPU

  • The number of intermediate objects created for complex graphics

  • The fraction of total screen pixels painted per frame

The first two items are the most important. When your program is running on the phone these two numbers should both be in the region of 30 frames per second. It is probably best to use these numbers (and the others) in comparative ways: Get accustomed to what the numbers are like in relatively simple programs, and then observe the changes as the program gets more complex.

The second diagnostics flag is:

Application.Current.Host.Settings.EnableRedrawRegions = true;

This flag is rather fun. Whenever the UI thread needs to rasterize graphics for a particular region of the video display, that region is highlighted with a different color. (These are sometimes called “dirty regions” because they need to be refreshed with new visuals.) If you see a lot of flickering over wide areas of the video display, the UI thread is working overtime to update the display. Optimally, you should be seeing very little color flashing, which is the case when the render thread is performing animations rather than the UI thread.

Here’s the third flag:

Application.Current.Host.Settings.EnableCacheVisualization = true;

This flag uses a color overlay to highlight areas of the display that are cached to bitmaps. You might want to try this flag with the RotatedSpiral program (the version that uses CompositionTarget.Rendering). When you run the program as it’s shown above, the whole display is tinted, meaning that the whole display needs to rasterized every time the Polyline changes position. That takes some times, and that’s why the performance is so poor. Now set:

polyline.CacheMode = new BitmapCache();

Now you’ll see the spiral in a tinted box, and the box itself is rotated. That’s the cached bitmap.

Other -----------------
- Animations - XAML-Based Animations
- Animations - Some Variations
- Animations : Click and Spin
- Animations : Frame-Based vs. Time-Based
- Raster Graphics : Becoming a Photo Extras Application
- Raster Graphics : Saving to the Picture Library
- Raster Graphics : Images and Tombstoning
- Raster Graphics : Vector Graphics on a Bitmap
- Raster Graphics : The Pixel Bits
- Raster Graphics : WriteableBitmap and UIElement
 
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
 
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server