Canvas Animation Trouble with Invalidate vs Refresh

Turn off transparent on that canvas to remove its flicker.

Yes, turning off transparent removed the flicker. Thanks Julian!!!

Perhaps turn off transparent and erase background
And double buffer on
It doesn’t flicker at all on my Windows 10 VM - But I AM running that on a VM on my Mac so it actually benefits from that :stuck_out_tongue:

When you are going to draw 100% of the canvas then turning erasebackground off makes sense
Transparency is something that is not well handled on Windows unless you’re using WPF (WinForms fakes it at the cost of memory & some speed on large refreshes)
WPF basically functions more like OS X in terms of compositing

or turn on double buffer (which by its use, turns off transparency in windows)

no probs

All this is from memory and its late, so it may or may not be right but…

Basically, Canvas transparency (if you can call it that) in windows is done by allowing the stuff behind to draw (grey from the window background), then it draws the canvas over the top, this causes the flicker as its allowing the render of the grey background from the window to show to the user, then it draws on the canvas. This works “ok” for things such as drawing a small button when its clicked or something like that as you would probably not notice, but it fails when you need to do something like your demo there.

You can see this more easily by changing the background of the window to something garish like cyan, you can see that cyan appearing during that “flicker”.

Essentially there is a Window.Update (WM_PAINT) happening before your canvas even draws, this tells windows to paint the window now showing the grey of the background. Then the control is painted and no matter what tricks you employ you cannot (easily) get around that framework WM_PAINT sent to the window before you make your draw calls in the canvas.

Now what would have been nicer for the framework to do would be to render all that to an off screen image and paint it onto the window is one go, thus eliminating the flicker (double buffering). There will be some reason why Xojo chose this method, someone might let us know, or they might not :slight_smile:

And if anyone is reading this thinking of using Timers, whatever you do, please dont use a Timer of 0 to iterate animations (other than for testing). The timing/speed of your animations will be related to the number of times the timer can fire not the passage of time, so if the app is under increased load (or on a slower system) your animations will seem to slow down or become “gloopy” as the timing of the iteration is altered. Calculate your maths using delta timing.

(Flicker: part 2398)
Not sure if anyone ever mentions this, but one technique I found handy is to ditch the canvas…

if the window is going to paint first anyway, doesn’t it make sense to do the painting in the Window’s Paint event, onto the Window’s graphics, rather than stick a canvas control on top of it?

[quote=350047:@Jeff Tullin](Flicker: part 2398)
Not sure if anyone ever mentions this, but one technique I found handy is to ditch the canvas…

if the window is going to paint first anyway, doesn’t it make sense to do the painting in the Window’s Paint event, onto the Window’s graphics, rather than stick a canvas control on top of it?[/quote]

HA! So I’m not the only one doing that :slight_smile:

I knew writing that after waking up in the middle of the night would mean I miss something…

So, you can do all that I said above, or you can take the Canvas Timer code that Eugene posted above and just change Canvas1.Invalidate to Canvas1.Invalidate(false) as the default parameter true.

This only works in this example because the background of the canvas is being wiped by the white fillrect, but if you had a button over writing another button it would have the same effect (as long as transparency wasn’t an issue). If you didnt do that, you would have a trail of artifacts on a moving object (comment out g.FillRect 0,0,g.width,g.height to see this happen).

Or, you could do that Jeff and Markus talk about and paint right onto the window. A lot less bother, but not quite as easy to visualise (without putting controls on the window)

Reading this thread, I was curios about invalidate/refresh in canvas, I have an old project in 2015 frame based animation, looks great at 60FPS and use Invalidate, just for fun I wish to know how fast could be the refresh, don’t work, so change the code and shows 400-450 frames per second with invalidate and refresh, don’t shows variances and I use a loop… The bad thing is don’t works in xojo2016x xojo2017x. in windows, but in mac works in 2017r2. could be the direct2d.

Here is what I am proposing for a summary of this conversation with three attached examples that all use loops (not a timer for Windows):

  1. CanvasSpeedTest1 - Uses a Canvas, with double buffer on, Erasebackground off, transparent off, and a loop which refreshes every 10th cycle: Average speed on my computer is 423 frames-per-second
  2. CanvasSpeedTest2 - Uses a subclassed Canvas, with double buffer on, Erasebackground off, transparent off, and a loop which refreshes every cycle: Average speed on my computer is 79 frames-per-second.
  3. WindowSpeedTest3 - Uses a Window, no doublebuffer, erasebackground, or transparent functions, and a loop which refreshes every cycle: Average speed on my computer is 66 frames-per-second.

Below is the download for the example programs, and the programs were run on Xojo 2017 r 2.1, on Windows 10 Book (no virtual machine).
WinCanvSpeedTest

Conclusion: Drawing with a Canvas on Xojo for Windows looks like it still needs some optimization the ‘magic’ hasn’t been found yet. Invalidate works with a timer, which is not as good on Windows, and is likely good on Mac (speculation). To minimize flickering, turn off Canvas Erasebackground, turn off Canvas Transparent, turn on Doublebuffer.

Thanks everyone!!!

In your Test 1 and Test 2 you might as well turn double buffer OFF because you’re manually double buffering and adding the extra double buffering does nothing for you
It wont change the frame rate much
Constant refreshes are the big penalty in most cases

Using timers isn’t the trick
The first and second test vary in that they redraw vastly different numbers of times
Using a timer doesn’t change that
If you used a timer that invalidated every 10th time I would expect it to run about as fast as, or faster than, the loop refreshing every 10th time
Refreshing every time vs every 10th means the workloads are different

FWIW fast frame rates arent that useful if your monitor cant refresh fast
A really fast monitor like the Acer Predator X35 or the ASUS ROG Swift PG35VQ are fast at 240 hz and anything above that for FPS is not useful - you literally cant see it
Common refresh speeds are still between 60 and 120 hz

[quote=350172:@Norman Palardy]In your Test 1 and Test 2 you might as well turn double buffer OFF because you’re manually double buffering and adding the extra double buffering does nothing for you
It wont change the frame rate much
Constant refreshes are the big penalty in most cases[/quote]
Yes, your right, there is no measurable change when double-buffer is on or off.

[quote=350172:@Norman Palardy]Using timers isn’t the trick
The first and second test vary in that they redraw vastly different numbers of times
Using a timer doesn’t change that
If you used a timer that invalidated every 10th time I would expect it to run about as fast as, or faster than, the loop refreshing every 10th time
Refreshing every time vs every 10th means the workloads are different[/quote]

Here is a rework of the program using timers and drawing with invalidate:
CanvasTimer1

The maximum frames-per-second I can achieve is 60 and the minimum for something like virtual reality is at least 90 frames-per-second, otherwise you get bad motion sickness. Is there another way to have the invalidate fire without a timer to help increase the framerate and yet have a smooth rendering of graphics? The motion of the circles is much smoother with this method.

[quote=350172:@Norman Palardy]FWIW fast frame rates arent that useful if your monitor cant refresh fast
A really fast monitor like the Acer Predator X35 or the ASUS ROG Swift PG35VQ are fast at 240 hz and anything above that for FPS is not useful - you literally cant see it
Common refresh speeds are still between 60 and 120 hz[/quote]

Yes, I completely agree that monitor refresh rates matter, and an older monitor from about 5 years ago will likely be 60-120 Hz. The minimum target is 90 Hz for the Vive and Oculus, and a bucket should be strategically placed on the floor if you try Virtual Reality with less than 90 Hz because of motion sickness :slight_smile:

I tried all Eugine’s examples each for several minutes and i reach a little higher framerates but not much (about 5 %). What i did notice is that although Xojo said that Xojo uses hardware speedup for graphics, these examples didn’t stress the videocard because the temperature didn’t raise over the usual 28 degrees Celsius on my system, only the temperature of the processor raised a little.

I think that Xojo has to investigate why this is the case and please on real hardware and not those dammend Mac flattering vm’s.

[quote=349956:@Eugene Dakin]HI Dave,

Thank you very much, as this is very helpful!

How would I be able to get the best-of-both worlds, meaning the speed of invalidate with the animation effect of refresh? My initial thought would be to have half of the Canvas calls be invalidate and half be refresh, although. I am just experimenting to try an optimize invalidate speed with refresh screen animation.

Once again, thanks![/quote]

I would prevent unnecessary refreshes to hit the canvas. For instance, if you are comfortable with a fresh every 1/60th a second which is the TV refresh rate, you could have a myRefresh method that checks one tick has elapsed to trigger a refresh.

You would have to use Microseconds for shorter periods.

Maybe something like this (not tested)

[code]sub MyRefresh()

static previousTicks as single
if ticks > previousTicks then
Canvas1.Refresh
previousTicks = Ticks
else
Canvas1.invalidate // In case it is the last drawing
next

end sub[/code]

if ticks > (previousTicks + <somevalue>)    

?

[quote=350235:@Jeff Tullin]if ticks > (previousTicks + <somevalue>)
?[/quote]

Why not, but in the code I posted, what we want to avoid is simply that no Refresh be performed before 1/60th a second minimum has elapsed.

So written as you post, it would be

if ticks >= (previousTicks + 1)

That would be of more interest if you want a frame rate of 100 pictures per second, no faster.

[code]sub MyRefresh()

static previousMS as double
if Microseconds >= (previousMS+10000) then
Canvas1.Refresh
previousMS = Microseconds
else
Canvas1.invalidate // In case it is the last drawing
next

end sub[/code]

why not create a Boolean flag “needsRefresh” that is set anytime something changes
then have a time set to 1/60th of a second with an Action Event that says

if needsRefresh then   
   canvas1.refresh
   needsRefresh=false
end if

I have worked on this a little more using Windows declares and comparing speed to invalidate and refresh, and I can’t seem to detect any measurable difference.

I determined a way to get out of a tight loop while the Canvas Refresh command is continuously fired. Here is an example that uses the Keyboard.AsyncKeyDown command to exit the tight loop when the user presses the small case ‘a’ when the circles are being drawn.

CanvasSpeedExitTightLoop

Some interesting thoughts were calling invalidate right before refresh. This didn’t seem to have any difference in frame rate. Time-clamping seems like it will smooth out the irregularities in redrawing times - maybe :slight_smile:

I am still working on it…

I would believe calling Refresh while the canvas has not finished will create a collision that could be source of flicker. Indeed, clamping will probably alleviate flicker. As counter intuitive as it is, slowing down display makes it look smoother. I use that trick in RubberViews to prevent some of the flicker during a resize.

This is interesting. I converted the example to OpenGL and used the same picture creating algorithm and the average frames-per-second that I am able to get is about 60.

OpenGl is known for begin fast, and my guess is that creating a picture and drawing to the picture is slowing things down.

Here is a summary so far:

  1. Canvas Refresh, shows animation throughout the drawing process and is slow (79 fps) -not smooth animation
  2. Canvas invalidate loop is fast and only shows the drawing at the end of the loop (>1000 fps) -no animation
  3. Canvas invalidate timer is slow because of the timer limit (60 fps) - has smooth animation
  4. Canvas/OpenGL loop is slow (60 fps) uses picture

Below is the link download the Canvas/OpenGL example:

CanvasSpeedTestOpenGL

It seems that creating a manual picture buffer in Xojo is slowing the process down, and options for the canvas (loop vs timer) have low frames-per-second.

I am going to try something else out with OpenGL and see if I can increase the drawing speed…

This is an interesting experiment so far :slight_smile: