WPF charting/visualization of realtime data

青春壹個敷衍的年華 提交于 2019-12-03 06:18:15

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.

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.

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]

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:

  1. Using WPF geometry.
  2. Using Direct2D.

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:

  • More than 1 billion data points.
  • Refresh rate of 5FPS.
  • 10,000 new data points being pushed at 100hz.
  • 10% CPU on an i7 Core laptop.
  • The WPF UI and Rendering threads are completely free to do other operations.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!