Note: I posted a similar question on the OS X channellink text. Many good answers, but no solution, so I’m hoping for help here. This program is/was used in development of a circulatory assist device.
My program both receives and can simulate blood pressure and ECG data, then makes a continuous 2D plot of the waveform . It plots new data point every 5 milliseconds. Program worked great in recent past. I was pulled to another project for a month. Came back to this and suddenly plotting has slowed to a crawl. Profiling shows that routine that draws waveform on a Canvas has slowed from 1-2 milliseconds in past down to 14 milliseconds now - much to slow to handle the data arriving every 5 milliseconds. I have not changed the Xojo code or configuration. Was using less than current IDE, so upgraded to 2015 v3.1 with no change. I did upgrade OS from Mavericks to El Capitan in the interval.
I have posted a stripped-down version of the project herelink text:
The main window is TriggerTestW. The action happens in it’s only Canvas (GraphCanvas). There is a timer called SampleTimer that fires every 5 milliseconds and gets a new data point for a simple simulated blood pressure curve. It Invalidates a small rectangular portion of GraphCanvas. GraphCanvas’s Paint event calls PlotDataSamples to draw the point on the curve.
The plot should appear with 70 pulses per minute, i.e. less than 1 second between peaks. It used to do that just fine. Now it requires about 3.5 seconds between peaks. Profiling shows that PlotDataSamples is taking >14 milliseconds, well below the 1-2 milliseconds it used to take.
Things already tried:
- Compiled versus IDE -> same problem
- 64 vs 32 bit -> no change
- Set eraseBackground, doubleBuffer & transparent on the canvas to false -> No change. - Made sure the canvas isn’t overlapping any other control - It’s not.
I’m looking for a Mac running an earlier version of OS X.
Runs dandy even in debug on 10.9.5
and the exact same machine running the exact same project with the exact same copy of xojo
JUST runnning 10.11
obviously slower - haven’t looked in your code at all to see what if anything might be altered to improve that
but it certainly suggests something in the OS is radically different
All right, then. Thank, you, Norman. Just to confirm, you’re seeing a new peak every second or so, correct? It definitely runs at about a third that speed on 10.11.1.
If so, that seems to point to a bug in El Capitan or an incompatibility between Xojo and the new OS. I hate to think about trying to get either of those fixed. Does anyone have any other ideas?. I hate to blame the compiler. Plus, I suspect chances of a rapid fix would not be high if they are not being flooded with complaints.
Its really pointing out that trying to draw every 5 msec is probably pointless
Receiving data points every 5msec is feasible but drawing every time you get one isn’t
That would require a refresh rate far beyond whats available anywhere
That’d be something like 1000 200 fps
And with integral positions for pixels you’re probably drawing over the same point on the screen many times
[quote=229048:@Norman Palardy]Receiving data points every 5msec is feasible but drawing every time you get one isn’t
That would require a refresh rate far beyond whats available anywhere[/quote]
You are certainly correct that I should collect the points together and draw less frequently. It is, I suppose, lazy programming to do it as I did but, nonetheless it did. @Will Shank made the point on the OS thread that the paint regions from multiple Invalidate events are being coalesced into a single Paint area, then all being updated at oncelink text Or, at least they must be when running on the older OS.[quote=229048:@Norman Palardy]And with integral positions for pixels you’re probably drawing over the same point on the screen many times[/quote]
That’s certainly occurring at times, but not always. When writing the program originally, I was sure this would happen, so only plotted every other point. There were noticeable gaps in the plot at the portions of the waveform with steep slopes. This was more prominent when plotting ECG date, but was also evident with the pulse data in the striped-down code. I would have thought that using DrawLine as I do, the line-drawing algorithm would have filled in the gaps, but it didn’t. Drawing lines to every point resulting in clean gapless curves. Other than laziness, that is another reason I coded it as I did.
Lines would work but drawing far in excess of 60 times a second just doesn’t seem that useful
That link points out that Apple has made changes in how the OS handles updates and I suspect this is precisely what you’re running into
What happens if you change the period you redraw from 5 to something like 1/60th of a sec ?
About the biggest side effect would be having to split data collection from drawing
I admit to some laziness in coding that. It was easier to just put out the plotting commands and let the OS coalesce them and do them when it refreshed the screen, as described in the excellent link you provided. A key thing, I think, is that I do not flush the events to the screen (as I would if I used Refresh rather than Invalidate). My understanding was that this difference is the reason for the two different commands - Refresh forces a flush, Invalidate doesn’t. I suspect that if that was not the case, the code would never have run briskly on the older OS.
I will download the Quartz Tools tomorrow and see what they tell me. I did notice that the last revision of the\at Apple document was a while ago, so the cautions it gives should have applied when the code ran on 10.9.5 as much as for 10.11.1. Unless, of course, something has changed in the way these events are handled.[quote=229062:@Norman Palardy]What happens if you change the period you redraw from 5 to something like 1/60th of a sec ?[/quote]
If I can’t get the old way working again, I’m sure that’s what I’ll need to do. In the worst case, I’ll skip points, but that will make a must less acceptable display than was working on the older OS.
You likely won’t have to skip points or have it appear any different at all. Your code has just been drawing more than was ever being displayed anyway. With El Capitan causing the invalidated views to be drawn on each timer action, it’s just another reason to clean up the code a bit- and you’ll be good to go.
You asked why other projects that animate like games etc are still fine- it’s because they don’t over-draw.
Maybe two timers? One at 5ms to get and collect the data and another at a slower rate (20ms?) to update the display.
Thank you, @Norman Palardy and @Travis Hill . So, is this definitely a change that occurred with El Capitan? This code did work fine up till that.
Sure seems like it since I can replicate this issue with everything the same JUST the OS version changing
I just reboot my machine into 10.11 and it does what you note
Come back to 10.9 and things are fine again
A 30 frame per second screen refresh rate means 33.3 ms between refreshes. Since the data comes in at regular 5 ms intervals, I guess I can probably just collect 6 or 7 points into an array, then draw them all in a single Paint event. I will try that first thing in the morning.
If that does it, you guys may have solved a major mystery! @Norman Palardy, could I please ask (another) favor of you? Could you run the program in 10.9 with the Code Profiler on. On 10.11.1, there is one PlotDataSamples call per SampleTimer event, supporting the idea that multiple invalidations are NOT being combined into one. If, on 10.9, there are multiple SampleTimer actions per call to PlotDataSamples, I think theory will be proven.
You guys are terrific, BTW.
There are, in my quick little trial, 2236 calls to SampleTimerAction with only 667 calls to GraphCanvas.Paint
So definitely not 1:1
That’s it, then!! Even a hard-head like me has to believe it. You have definitely solved the problem!
Thank you so very much for taking the time and effort to help me with this. You are scholars and gentlemen and I am very grateful.
BTW, every one of you guys deserves a raise.
My 2cents… .while some have mentioned using 30frames per second… you might also try 24 or 25 frames instead.
The human eye is usually good for 24 frame animation… plus if you use 30 frame, and your monitor has a 60 frame (hz) refresh you might get flicker if the cycles coincide just wrong
I’ll try it out.
In the meantime, I tried drawing the points in groups of 10 (i.e. 50 milliseconds of data) and it works beautifully. Average execution time for PlotDataSample has gone from >14 ms down to 0.4 ms. With a few optimizations, I think I can even cut that in time. Plus, now PlotDataSample is only called one-tenth as often. So total speed-up is 2 orders of magnitude!
Thank you once again to all who have helped on this, especially @Norman Palardy.