ScottPlot and WPF background rendering - c#

ScottPlot is used to display a real time chart with 10'000+ data points.
Datapoints are added every seconds, so the chart needs to be updated.
A timer perform the refresh every seconds, however, it freezes the whole UI for a few ms, which is annying.
ScottPlot does have a Chart.RenderRequest(); method which is non-blocking, but the chart is still rendered on the UI thread so that does not solve the issue.
And of course, if we try to refresh it from a background worker, a thread exception happens.
var bg = new BackgroundWorker();
bg.DoWork += (s, e) =>
{
Chart.RenderRequest();
};
bg.RunWorkerAsync();
Is there any ways to render the chart in a separate thread with WPF and ScottPlot ?
Edit:
Did timing analysis, Refresh, Render, RefreshRequest, RenderRequest all have similar execution time, despite the "request" are supposedly non-blocking.

Some time ago I faced a similar issue.
I had a lot of data to be displayed in multiple charts, I found that (in my canse) an acceptable limit to have a satisfying user experience is to limit the data rendered by scottplot to about 1 million points.
Here there are some tips that maybe can help you to solve your issue.
If you can, use SignalXY plot. It is designed to render a lot of data, so the rendering is much faster than using any other type of graph.
Unfortunately you must render the graph in the UI thread, but you can limit the use of the UI thread to the call wpfPlot.Refresh(). Create one pair of double array double[] Ys and Xs[] of the maximum size that your data can have. Create a plot of the desired type and keep the instance in your class as a class variable (same thing for the other plottables elements, like crosshair or legend). When you need to update data just change the value of Ys and Xs. Do not add and remove the plots from scottplot wpfPlot.
I write a small example below, just to give you an idea:
double[] times;
double[] values;
SignalXY plot;
private void Initialize()
{
wpfPlot.Plot.Clear(); //This removes all plottables
times = new double[MaxSamples];
values = new double[MaxSamples];
plot = wpfPlot.Plot.AddSignalXY(times,values);
}
private void UpdatePlot()
{
int counter=0;
foreach(var data in myData)
{
times[counter] = data.Time;
values[Counter = data.Value;
counter++;
}
//This tells to scottplot to render only until the index counter, so you
//can have a bigger Xs arrays that can be created only at the beginning
plot.MaxRenderIndex=counter;
//This call must be done in UI thread
wpfPlot.Refresh();
}
These are the two changes that, for me, improved the rendering performances by A LOT. Hope that can be useful for you too.

Related

How do I stop scheduled UI updates if I get new ones?

So, I have a resizable window which draws a graph given on live values it calculates. If I make the window bigger, it redraws and scales everything, also when the graph hits a new maxY and maxX, using those as reference points to determine the position of the other points. This works fine, but if I resize the window, and a lot of things are being scheduled for updating, it the window resizes fine, but the Graph mimics what it was supposed to do a couple (up to 4) seconds ago.
If I understand this correctly, every time I invoke my UI thread, which is blocked, and give him new coordinates to draw, it finishes the current one and then goes on to the next one. I see why you would want that, but since the graph is more or less constantly scaling, it is deleting itself every update anyway, so I would save quite some processing power and runtime, if I would just finish every current task and jump over all the accumulated task right to the newest one, since the stored ones are outdated anyway
Is there a way to do this?
I thought of 3 things which might work, of which the third one the only one is, I know will work or, rather, is possible, even if rather slow:
// (1) If there is a way to check if the UI thread is busy, I could just not send
// a new request to draw another line
if(!uithread.busy)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => this.DrawGraph(new List<List<Coordinates>> { CoordinateListA, CoordinateListB })));
}
// (2) As soon as UI thread finishes the current task, it jumps to the newest one
// (3) In my opinion the ugliest solution, I check how long the last drawing took and
// don't send any draw-requests in that time. I will just wait that certain time and
// just then send him the newest results to draw and measure how long that took
If there is no better solution, I think I will go with (3), but since I hope that there is another way, I wanted to ask if maybe someone else here had a similar issue.
So I fixed it. My Architecture saves all the logs, so I do not need to save coordinates, if I need new values I can just, every time I need new Coordinates, calculate them from the Logs. Like this:
// beginning of the class
private bool _isDrawing = false;
if (!_graphIsDrawing)
{
_graphIsDrawing = true;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => this.DrawGraph(new List<List<Coordinates>> { CoordinateListA, CoordinateListB }, scalingFactors, canvasSize)));
//I need to invoke, since I am working with multiple threads here. Else it
//would be enough to just call 'this.DrawGraph(...)'
}
///////////
public void DrawGraph(List<List<Coordinates>> listOfGraphs, float[] scalingFactor, int[] canvasSize)
{
lock (_graphDrawLock)
{
this._AlgorithmRuntimeViewA.DrawGraph(listOfGraphs[0], scalingFactor, canvasSize);
this._AlgorithmRuntimeViewB.DrawGraph(listOfGraphs[1], scalingFactor, canvasSize);
_graphIsDrawing = false;
}
}
Here I lock it again, so not both threads draw at the same time breaking everything. At the end I set _graphIsDrawing to false again, so I can call it again.

How do I show a progress bar with the actual percentage of progress of my background worker?

I have a background worker that executes 2 functions, and I don't know if there is a way to measure how much does it takes to complete the process.
For example:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
table1 = function1;
table2 = function2;
}
I have found this
int total = 100; //some number (this is your variable to change)!!
for (int i = 0; i <= total; i++) //some number (total)
{
System.Threading.Thread.Sleep(100);
backgroundWorker1.ReportProgress(i);
}
Which is not representative to the progress rate, I have seen the progress bar fill and restart again and the work ends somewhere along the second or third "run"
I want the progress bar to actually show how much is done in %
is that possible or I have to guess by trial and run?
Unfortunately, there's no way to do this in an automatic way (as up to my knowledge).
It is you as a programmer who defines the progress represented in a progress bar.
The usual example is a background process iterating a large array. The progress bar is updated, for example, in 10% portions when the background proces is done with every 1/10th of an array. Or in 5% portions for every 1/20th of an array and so on. For an example of this you can refer to the link provided by #SergeyShevchenko in the comments.
At the very least (in a not very representative way though) you could decree that the first function comprises approximately 50% (20? 80%?) of the whole work and update the progress bar accordingly after the first function is done before calling the second function.
Another approach would be to change the two functions (if it is possible and reasonable) in a way that they're able to report their progress, then you would be able to translate the reported progress into ProgressBar state.

A few doubts about charts in C# .NET

I have a few question about charts in .NET in C#, I'm currently working on an app which will display data comming from serial port in real time.
I'm using a simple chart from .NET toolbox with spline series and there are two main feautures which I want to achieve.
The first one - I'd like the comming data points to be displayed on the right part of the chart and I'd like them to move to the left as the new data comes, I don't know how to describe it properly, I hope you can get the main idea since I can't post images yet.
I've achieved this kind of behaviour by reloading all the data points each time I refresh the chart, at first they are all empty and as the data comes I put the bigger and bigger queue of values in the end.
To clarify that the code may be like that:
chartAccelerationX.Series["XSeries"].Points.Clear();
dataPointTable = dataPointQueue.ToArray();
for (int i = 0; i < 1000; i++)
{
DataPoint dataPointX = new DataPoint();
if (1000 - dataPointTable.Length < i)
{
dataPointX.SetValueXY(i + 1, dataPointTable[i - 1000 + dataPointTable.Length].x);
}
else
{
dataPointX.IsEmpty = true;
}
chartAccelerationX.Series["XSeries"].Points.Add(dataPointX);
}
chartAccelerationX.Update();
It works pretty well, but as I said, I'm creating all the 1000 data points each time I update the chart (and I do it every 100ms) so it's probably disastrous in terms of performance (I'll need to have about 6-8 charts in total) and it's limited by the exact number od data points (1000 here).
Isn't there any easier way to achieve something like that with increasing amount of data points dynamically and with the second feature that I wanted to have - an auto matching scroll bar showing for example only 50 records at once?
I've been using a scrollbar as well, but it was just set to show 50 records out of 1000 and I could have scroll through all these empty data points right now.
I can probably make the scrollbar appear after an exact number of data points are on the chart and update it every time the data is added but maybe theres any easier way?
I hope you understood what I was trying to say, I'm still working on my English.
If you can use WPF, there is http://dynamicdatadisplay.codeplex.com/ and for the older WPF version http://dynamicdatadisplay.codeplex.com/wikipage?title=D3v1
which is pretty good, I've used it for some pretty high speed data sampling before

How can I constantly take screen shots without killing performance?

The requirement is like this:
I must issue a command requesting a screenshot. The twist? The command specifies a time - and that time is in the PAST. Not forever, mind you, just within a 30 second window.
My command indicates the actual second, and the corresponding screenshot is pulled from the rolling cache and saved in a permanent location for another process.
To accomplish this, I must take a screenshot every second, and I must preserve them for 30 seconds. After that the cache can purge itself.
My question is
What method for screenshots is least impactful against the desktop?
I can't imagine that one frame per second is going to be terrible for performance. Have you tried the simplest possible method to see if it impacts performance?
Take a look at http://www.dotnetjalps.com/2007/06/how-to-take-screenshot-in-c.html for information on how to get a screen shot.
I would suggest you use a timer:
var ssTimer = new System.Threading.Timer((s) =>
{
GetAndStoreScreenShot();
}, null, 1000, 1000);
You'll need some synchronization on your cache to prevent threading problems (i.e. trying to read the first item in the cache while it's being pushed out). Probably easiest to use a lock, since that's not going to be particularly performance sensitive.
As for what to use for a cache, I'd suggest a LinkedList. It's easy to append to the list (AddLast()) and remove the first item (RemoveFirst()), and since you'll only have 30 of them and requests for screen shots will be relatively infrequent, a sequential scan to get the nth item wouldn't be too time consuming.
Or you can implement a simple circular buffer using List or Array. Increment an index to be the insertion spot, wrapping around when you increment off the end. A lookup then becomes a simple matter of modulo arithmetic.
Along the lines of what Jim said, you could store your working set of screenshots in a ConcurrentDictionary<DateTime, Bitmap>, and have two timers: The first timer would add your screenshot to the dictionary, and the second would remove anything older than 30 seconds from the dictionary.
However, this is going to be terrible for performance pretty much any way you slice it.
Something along these lines:
var imageDictionary = new ConcurrentDictionary<DateTime, Bitmap>();
var screenshotTaker = new System.Timers.Timer(1000);
screenshotTaker.Elapsed += (sender, e) =>
{
Bitmap bmp = GetScreenshot();
imageDictionary.TryAdd(DateTime.Now, bmp);
};
var screenshotRemover = new System.Timers.Timer(1000);
screenshotRemover.Elapsed += (sender, e) =>
{
RemoveExpiredBitmaps();
};
screenshotTaker.Start();
screenshotRemover.Start();

Displaying images with a high frame rate

here's the problem: I have a custom hardware device and I have to grab images from it in C#/WPF and display them in a window, all with 120+ FPS.
The problem is that there is no event to indicate the images are ready, but I have to constantly poll the device and check whether there are any new images and then download them.
There are apparently a handful of ways to do it, but I haven't been able to find the right one yet.
Here's what I tried:
A simple timer (or DispatcherTimer) - works great for slower frame rates but I can't get it past let's say, 60 FPS.
An single threaded infinite loop - quite fast but I have to put the DoEvents/it's WPF equivalent in the loop in order for window to be redrawn; this has some other unwanted (strange) consequences such as key press events from some controls not being fired etc..
Doing polling/downloading in another thread and displaying in UI thread, something like this:
new Thread(() =>
{
while (StillCapturing)
{
if (Camera.CheckForAndDownloadImage(CameraInstance))
{
this.Dispatcher.Invoke((Action)this.DisplayImage);
}
}
}).Start();
Well, this works relatively well, but puts quite a load on a CPU and of course completely kills the machine if it doesn't have more than one CPU/core, which is unacceptable. Also, I there is a large number of thread contentions this way.
The question is obvious - are there any better alternatives, or is one of these the way to go in this case?
Update:
I somehow forgot to mention that (well, forgot to think about it while writing this question), but of course I don't need all frames to be displayed, however I still need to capture all of them so they can be saved to a hard drive.
Update2:
I found out that the DispatcherTimer method is slow not because it can't process everything fast enough, but because the DispatcherTimer waits for the next vertical sync before firing the tick event; which is actually good in my case, because in the tick event I can save all pending images to a memory buffer (used for saving images to disk) and display just the last one.
As for the old computers being completely "killed" by capturing, it appears that WPF falls back to software rendering which is very slow. There's probably nothing I can do about.
Thanks for all the answers.
I think you're trying for too simplistic of an approach. Here's what I would do.
a) put a Thread.Sleep(5) in your polling loop, that should allow you to get close to 120fps while still keeping CPU times low.
b) Only update the display with every 5th frame or so. That will cut down on the amount of processing as I'm not sure that WPF is made to handle much more than 60fps.
c) Use ThreadPool to spawn a subtask for each frame that will then go and save it to the disk (in a seperate file per frame), that way you won't be as limited by disk performance. Extra frames will just pile up in memory.
Personally I would implement them in that order. Chances are a or b will fix your problems.
You could do the following (all psuedocode):
1. Have a worker thread running dealing with the capture process:
List<Image> _captures = new List<Image>();
new Thread(() =>
{
while (StillCapturing)
{
if (Camera.CheckForAndDownloadImage(CameraInstance))
{
lock(_locker){_captures.Add(DisplayImage);
}
}
}).Start();
Have the dispatcher timer thread take latest captured image (obviously it will have missed some captures since last tick) and display. Therefore, UI thread is throttled and doing as little as possible, it isn't doing all the "capturing", this is done by worker threads. sorry I can't get this bit to format (but you get the idea):
void OnTimerTick(can't remember params)
{
Image imageToDisplay;
lock(_locker){imageToDisplay = _captures[k.Count - 1];
DisplayFunction(imageToDisplay);
}
it might be that the list is a queue and another thread is used to bleed the queue and write to disk or whatever.

Categories

Resources