Canvas Animation Trouble with Invalidate vs Refresh

  1. ‹ Older
  2. 6 days ago

    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.

  3. Eugene D

    Sep 13 Pre-Release Testers, Xojo Pro Canada scispec.ca

    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!!!

  4. Norman P

    Sep 13 Xojo Inc
    Edited 6 days ago by Norman P

    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

  5. Eugene D

    Sep 13 Pre-Release Testers, Xojo Pro Canada scispec.ca
    Edited 6 days ago by Eugene D

    @Norman P 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

    Yes, your right, there is no measurable change when double-buffer is on or off.

    @Norman P 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

    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.

    @Norman P 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

    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 :)

  6. Andre K

    Sep 14 Pre-Release Testers, Xojo Pro

    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.

  7. 5 days ago

    Michel B

    Sep 14 Pre-Release Testers, Xojo Pro
    Edited 5 days ago by Michel B

    @Eugene D 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!

    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)

    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
  8. Jeff T

    Sep 14 Midlands of England, Europe

    if ticks > (previousTicks + <somevalue>)
    ?

  9. Michel B

    Sep 14 Pre-Release Testers, Xojo Pro
    Edited 5 days ago by Michel B

    @Jeff T if ticks > (previousTicks + <somevalue>)
    ?

    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.

    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
  10. Dave S

    Sep 14 San Diego, California USA

    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
  11. Eugene D

    Sep 14 Pre-Release Testers, Xojo Pro Canada scispec.ca

    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 :)

    I am still working on it....

  12. Michel B

    Sep 15 Pre-Release Testers, Xojo Pro

    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.

  13. 4 days ago

    Eugene D

    Sep 15 Pre-Release Testers, Xojo Pro Canada scispec.ca

    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 :)

  14. Tim H

    Sep 15 Pre-Release Testers Portland, OR USA

    @Eugene D Canvas invalidate loop is fast and only shows the drawing at the end of the loop (>1000 fps) -no animation

    @Eugene D It seems that creating a manual picture buffer in Xojo is slowing the process down

    These 2 statements appear to contradict each other. If you can create 1000 pictures per second without drawing them, but significantly fewer when you put them on the screen, wouldn't that indicate that the problem lies in the screen update?

  15. Eugene D

    Sep 15 Pre-Release Testers, Xojo Pro Canada scispec.ca

    Hi Tim,

    Ah yes, your right.

    DrawFilledCircle(MyCircles(i).Left, MyCircles(i).Top, MyCircles(i).CircleDiameter)

    Drawing with invalidate is creating pictures fast, and showing the pictures on the Canvas is slow... Good catch!

    It seems that creating a manual picture buffer in Xojo is slowing the process down

    I think I would be correct in saying that updating the canvas with the picture using refresh is slowing the process down.

    Does that make more sense? Ok.. going to get more coffee :)

  16. Dave S

    Sep 15 San Diego, California USA

    @Eugene D Drawing with invalidate is creating pictures fast, and showing the pictures on the Canvas is slow... Good catch!

    This makes no sense.... UNLESS you are drawing in a PICTURE object and NOT directly to the canvas during the PAINT event
    IF you are drawing directly in the Paint Event, then "creating" and "showing" the picture is virtually the same process.
    Remember you can issue 1000 "invalidate" during a run cycle, but it will only refresh the canvas ONCE, meaning the PAINT event is called ONCE...

    However if the PAINT event contains

    g.drawpicture myPicture,0,0

    and myPicture is drawn and redrawn 1000 times , and displayed once, then that statement makes a little more sense.

    Note : I have written Xojo programs that redrew (in the PAINT event) hundreds of objects, and was able to obtain 60FPS

  17. Eugene D

    Sep 15 Pre-Release Testers, Xojo Pro Canada scispec.ca

    Hi Dave,

    I like what your saying, and here is my interpretation.

    It appears that "creating" a new picture (Dim PBuffer as New Picture) is fast, and populating the picture is fast when drawing many objects (circles) on the picture.

    Once the picture has been created, then it is to be "shown" in the Canvas by copying the picture into the Canvas by drawing (g.DrawPicture(PBuffer,0,0,me.Width, me.Height)) which seems to be the slow step.

    @Dave S myPicture is drawn and redrawn 1000 times , and displayed once, then that statement makes a little more sense.

    This is what I meant ... and not necessarily what I said :)

    @Dave S Note : I have written Xojo programs that redrew (in the PAINT event) hundreds of objects, and was able to obtain 60FPS

    The example programs that are attached seems to start slowing down in drawing speed at about 400 objects. Usually below 400 objects the frame rate is about 60 fps, which reinforces what you said.

    Thanks!

  18. 3 days ago

    Eugene D

    Sep 16 Pre-Release Testers, Xojo Pro Canada scispec.ca
    Edited 3 days ago by Eugene D

    I think I have been able to get a little closer to having faster speeds with Xojo, and it looks like it might be with an OpenGLSurface that loads the circles into memory, and uses the GPU to perform the drawing function while the CPU performs calculations for the new position of the circles. The GPU can be forced to work hard, as I can hear the cooling fan start spinning.
    The code is not optimized, and it does seem to have quite a performance increase.
    Explaining running of the program:
    The default number of circles (ball) is 100. If you want to change this number, stop the program, go into the Xojo IDE and change the text to 200, or 50, or whatever number you would like. This step is needed because the circles have been loaded when opening the program. This can be changed in the future when given more thought (I am out of coffee at the moment)

  19. Norman P

    Sep 16 Xojo Inc

    @Eugene D I am out of coffee at the moment

    AAAHHHHHHHGGGGG !!!!!!!!!!! STOP CODING AND GET COFFEE !!!!!!!!!! :P

  20. Eugene D

    Sep 16 Pre-Release Testers, Xojo Pro Canada scispec.ca

    Hmm.. thats wierd.. Only half my message appeared.. With a fresh cup of coffee in hand, lets try this again... :)

    I think I have been able to get a little closer to having faster speeds with Xojo, and it looks like it might be with an OpenGLSurface that loads the circles into memory, and uses the GPU to perform the drawing function while the CPU performs calculations for the new position of the circles. The GPU can be forced to work hard, as I can hear the cooling fan start spinning.

    The code is not optimized, and it does seem to have quite a performance increase.

    Explaining running of the program:
    The default number of circles (ball) is 100. If you want to change this number, stop the program, go into the Xojo IDE and change the text to 200, or 50, or whatever number you would like. This step is needed because the circles have been loaded when opening the program. This can be changed in the future when given more thought (I am out of coffee at the moment). Here is the link to download the program.

    OpenGL GPU

    Number of Circles   Target FPS   Measured FPS
    300                      300         145
    200                      300         222
    100                      300         239

    Steps to change the Target FPS are: Change the number in the textbox, press 'Reset Ball' pushbutton, and wait until the animation stops. Press 'Run test' to start animation with the new parameters.

    The GPU is working harder because I can hear the GPU fan starting on my computer when running the program for a while - this is good news!

  21. Eugene D

    Sep 16 Pre-Release Testers, Xojo Pro Canada scispec.ca

    Something I forgot to mention was the OpenGLSurface fires on every render. No skipping of timing is involved. This is similar to the refresh event firing on a canvas all the time.

or Sign Up to reply!