Performance appliance of RenderTargetBitmap
Challenge: draw complicated Path and animate anything over it
Problem: high CPU while animating object
So, we got another challenge – draw Path object with thousand of points and animate another object over the Path by having least possible impact on CPU. What’s the problem?
First of all, let’s create out XAML
<Path Name="path" Stroke="Black" StrokeThickness="1" Data="{x:Static l:Window1.Data}"/>
and generate graphics geometry for it
public static Geometry Data
{
get
{
for (int i = 0; i < 1024; i++)
{
pts.Add(new Point((double)rnd.Next(2000), (double)rnd.Next(2000)));
}StreamGeometry geometry = new StreamGeometry();
geometry.FillRule = FillRule.EvenOdd;
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(new Point(0, 0), false, false);
ctx.PolyLineTo(pts, true, true);
}
return (Geometry)geometry.GetAsFrozen();
}
}
Next, let’s create simple rectangle and animate TranslateTransform object, relays in RenderTransform collection
redRect.Width = 100;
redRect.Height = 100;
redRect.Fill = Brushes.Red;
redRect.HorizontalAlignment = HorizontalAlignment.Left;
redRect.RenderTransform = trans;
…root.Children.Add(redRect);
DoubleAnimation da = new DoubleAnimation(0, this.ActualWidth, new Duration(TimeSpan.FromSeconds(10)));
da.Completed += (EventHandler) delegate {root.Children.Remove(redRect);};
trans.BeginAnimation(TranslateTransform.XProperty, da);
Animating RenderTransform collection is the most efficient way to change things in WPF visual tree. It does not tickles the tree, does not performs layout and rendering engine is very smart to update only dirty regions.
See yourself (dirty regions update is colored)
Very well. Let’s see CPU rate – 60%? WTF? Why? I’m using most performant RenderTransform, transforming only necessary things and doing it over static object. Static? Well, it’s retained object, so WPF trying to redraw each region under the moving object. What to do?
We can try to use Adorners. It might help us to avoid underlying object redraw.
So, we’ll create our own Adorner, that knows to hold UIElement inside it
class CanvasAdorner : Adorner
{
UIElement parent,child;
List<UIElement> children;
public CanvasAdorner(UIElement adornedElement, UIElement Child)
: base(adornedElement)
{
children = new List<UIElement>();
parent = adornedElement;
Add(Child);
}
Then we’ll create adorner layer from our root panel and animate rectangle inside it.
AdornerLayer layer = AdornerLayer.GetAdornerLayer(root);
CanvasAdorner ca = new CanvasAdorner(root, redRect);
layer.Add(ca);
DoubleAnimation da = new DoubleAnimation(0, this.ActualWidth, new Duration(TimeSpan.FromSeconds(10)));
da.Completed += (EventHandler)delegate { layer.Remove(ca); ca.Remove(redRect); };
trans.BeginAnimation(TranslateTransform.XProperty, da);
We should see performance boost? No, we’re not. This is because of rendering engine, that draws everything together
It still works very hard to redraw each pixel in geometry.
What to do? We can either create new DirectX surface and draw over it or, maybe another rendering (or UI) thread? Net very simple solution. I prefer “GDI+ way”.
What is it? Don’t you remember, old good way, when we pin pixels in bitmap in order to make visual cache? We can do it in WPF too. To do it, we’ll use RenderTargetBitmap object. This object knows convert WPF Visual into Bitmap and this is exactly what we need.
So we’ll create Image and it source will be our Visual, rendered by RenderTargetBitmap (this is software accelerated!)
RenderTargetBitmap rtb = new RenderTargetBitmap((int)path.ActualWidth, (int)path.ActualHeight, 96, 96, PixelFormats.Pbgra32);
rtb.Render(path);
rtb.Freeze();
root.Children.Remove(path);
Image img = new Image();
img.Stretch = Stretch.None;
img.Source = rtb;
root.Children.Insert(0,img);
Now we can move and animate our rectangle over bitmap. It still works and renders only necessary regions (BTW, pay attention, that those regions much smaller, thus animation performance is much better), but what’s happened with CPU?
It’s around 2% – we reach the goal. But why his happens? What the difference between rendering Path and Bitmap? After all both pixels – yes, but in Bitmap case those pixels are not retained, thus rendering thread almost does not work. See the CPU comparison for three of those methods
Don’t it really cool? There are some problems with this method – one of those problem (and most serious) is that creation of RenderTargetBitmap and it’s rendering takes a lot of time and CPU (in my case it was about 0.5 seconds) another problem, that we’re playing with Visual and Logical tree, so we’re rerender and layout whole application window twice – before and after creation. However even with those problems this way is the best to get performant animation over retained WPF objects.
Have a nice day and be good people. Source code for this article.
You may also be interested with:
- Real singleton approach in WPF application
- INotifyPropertyChanged auto wiring or how to get rid of redundant code
May 4th, 2008 · Comments (4)
4 Responses to “Performance appliance of RenderTargetBitmap”
Leave a Reply
Discover other tags
My tools
- .NET Framework Detector
- Duplicate images finder
- Exchange Security Policy for Windows Mobile Devices Fix
- Gas Price Windows Vista SideBar gadget
- Israel Traffic Information Windows Vista SideBar gadget
- Localization fix for SAP ES Explorer for Visual Studio
- LocTester
- RTL and LTR in Windows Live Writer
- Silverlight controls library
- Snipping tool integration plugin for WLW
- USB FM receiver library
- Vista Battery Saver
- WebCam control for WPF
- Windows Live SkyDrive attachment for Windows Live Writer
- Wireless Migrator
- WPF Virtual Keyboard




January 1st, 2009 at 12:47 am
Pingback from relays
January 1st, 2009 at 12:47 am
Pingback from regions net
April 6th, 2009 at 9:08 pm
Hi,
This article is very informative. I am facing similar issue. The problem I wanted to solve is to display multiple signals (channels) in real time. I need to update the data at 10 mm/second speed. I am able to achieve my goals using either rendertarget bitmap (by drawing path geometry on bitmap) or by using polyline directly on a grid control. The issue is code uses too much CPU. I was impressed with performance in your code when you are animating rectangle on a bitmap, but in my case it is not helping. Can you give me any clues on how I can enhance my code.
Thanks
Sathya
September 12th, 2009 at 8:13 am
Ohhh.. Perfect job ! thanks a lot.