I've been trying to figure out what is the appropriate way to render real-time data as a line graph in WPF. And by real-time I actually mean, data that is being collected from a USB device that generates data at a rate of approximately 40Hz. There are multiple (up to 7) streams of data that I'm reading at 40Hz in an asynchronous fashion.
I've tried using two off-the shelf solutions (WPF Toolkit charting and Swordfish charts) and almost looked into the Dynamic Data Visualization component but gave up on it after reading some comments on their forum. It seems that off-the-shelf charting solutions are geared towards static charts and I actually need something similar to the Windows Task Manager - only much faster and with way more data points.
Currently, I've rolled my own solution which seems to work the best so far but I have a feeling that I'm missing something because it seems that I should be able to get better performance out of it.
The requirements are that it should be able to handle a constant range of about 10000 points in a sliding window - as new data comes in (at 40Hz), old data get's pushed to the left outside of the visible range. And it needs to sustain this rate for at least 20 - 30 minutes (a total of about 75 - 100 thousand points per data stream).
My current custom implementation is a based on a component that inherits from Shape and uses a StreamingGeometry for the DefinigGeometry. The data coming in from the device is passed to the component via a queue to improve performance due to the inherent "bursting effect" and after a dequeue operation, the component is invalidated.
So, my question is, am I on the right path or am I completely wrong? What is the most efficient way to accomplish such data visualization in WPF? Any help or hints would be greatly appreciated.
As mentioned before, the WPF "standard" way of doing this will not get you the required performance. After trying several different free and commercial products and not getting what I needed, I started to experiment with the following methods:
Both of them were not performant at all.
One thing I know is that WPF is good at rendering images (BitmapSource), so I decided to go to that direction and use WriteableBitmapEx to draw the graph on a WriteableBitmap then pass it on...
One problem with the WriteableBitmapEx library is that it doesn't have much drawing features like, for instance GDI. So why not just use GDI? there is no difference if you do it right.
Example:
public void BeginDraw()
{
_writeable_bitmap = new WriteableBitmap((int)Math.Max(_size.Width, 1),
(int)Math.Max(_size.Height, 1), 96.0, 96.0, PixelFormats.Pbgra32, null);
_gdi_bitmap = new System.Drawing.Bitmap(_writeable_bitmap.PixelWidth,
_writeable_bitmap.PixelHeight,_writeable_bitmap.BackBufferStride,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
_writeable_bitmap.BackBuffer);
_writeable_bitmap.Lock();
_g = System.Drawing.Graphics.FromImage(_gdi_bitmap);
_g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
_g.Clear(System.Drawing.Color.Transparent);
}
public void DrawSeries(IEnumerable<System.Drawing.PointF> points)
{
_g.DrawCurve(dataSeries.GdiPen, points.ToArray());
}
public void EndDraw()
{
_writeable_bitmap.AddDirtyRect(new Int32Rect(0, 0,
_writeable_bitmap.PixelWidth, _writeable_bitmap.PixelHeight));
_writeable_bitmap.Unlock();
var cloned = _writeable_bitmap.Clone();
cloned.Freeze();
Dispatcher.BeginInvoke(new Action((() =>
{
Image = cloned;
})));
_g.Dispose();
}
As you can see I am using a "special trick" to draw the graphics directly to a native WPF BitmapSource using GDI.
I am not sure this is the proper way of measuring but using this method I have managed to get some very decent results:
I am the author of the open source library RealTimeGraphX (for WPF & UWP) that does exactly that. https://github.com/royben/RealTimeGraphX
The rest of the graph components except the actual line series, are made using standard WPF controls and shapes, so they can be customized and manipulated easily.
Disclosure: I own ABT Software and have developed SciChart, plus have contributed towards the WriteableBitmapEx open source library
Unfortunately you're not missing anything. The retained mode rendering engine in WPF / Silverlight delivers poor performance for this type of work. I've worked on a number of systems that were upgraded from Windows Forms to WPF where the client was sorely dissapointed by the rendering performance of this "GPU Accelerated" framework!
Anyway, there is a way. Use immediate mode rendering. Check out the WriteableBitmap or InteropBitmap classes. There is an excellent open source library out there called WriteableBitmapEx by Rene Schulte which I have contributed towards. WriteableBitmapEx provides some low-level drawing functions (GDI style) for drawing directly to bitmap. This delivers fantastic performance and low memory footprint (yes MS's fancy framework is beaten by a couple of well optimised for-loops and pointer to byte array).
If it is a specific third party chart component you're looking for, try SciChart. SciChart is a component I have developed myself which seeks to fill the gap for ultra high performance WPF or Silverlight scientific / stock charts. It uses proprietary resampling algorithms to reduce the dataset before drawing, immediate mode rendering and a host of other optimisations such as object pooling and resource re-use, resulting in smooth refresh rates for very large datasets and low memory footprint.
Click on the performance demo on the link above (requires Silverlight 4). Currently SciChart is able to render 1,000,000 datapoints at around 5FPS (depending on target hardware), equivalent to 5,000,000 datapoints per second. A commercial license will be available in Q1 2012.
Best results can be achieved by low-level DirectX programming, and utilizing HLSL shaders. System.Windows.Media namespace based rendering should be forgot instantly when max performance and real-time needs are important.
We were able to develop routines that can plot over 1000 million data points, e.g. 8 data feeds x 125 M data points, using wide line, no downsampling. The routines are part of LightningChart, WPF charts (and WinForms chart). It took like 7 years for us to get to this point... We made a billion points example, with VS project and Youtube video included.
[I'm tech lead of LightningChart]
The retained mode rendering of WPF makes drawing/redrawing custom charts and images tricky to make performant, especially when those drawings contain a lot of objects.
The fastest drawing I've been able to do in WPF is by using a WritableBitmap and filling it with a call to WritePixels, which may be an option for you. It vastly exceeded the drawing speed of the chart I wrote using PathGeometries and drawing to a Canvas.
I'm interested to see if there is a quicker middle ground.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With