Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: SetFramerateLimit() causes stutter & choppy animation  (Read 10129 times)

0 Members and 1 Guest are viewing this topic.

Mediocritus

  • Newbie
  • *
  • Posts: 4
    • View Profile
SetFramerateLimit() causes stutter & choppy animation
« on: January 23, 2012, 06:13:02 am »
I noticed something disturbing about sf::Window animation with OpenGL:  any call to SetFramerateLimit(), even with an absurdly high argument such as:

Code: [Select]

    //App.SetFramerateLimit(30);
    //App.SetFramerateLimit(60);
    //App.SetFramerateLimit(120);
    App.SetFramerateLimit(360);


will introduce irregularities & small pauses into the animation!  The rotation of even *a single quad* (2 triangles, no texturing) stutters very noticeably, approximately once every second on Windows 7 64-bit.

It was very surprising to find this line was the root cause of the problem.  My first suspect was UseVerticalSync(), and it's true that also introduces a little bit of stutter -- but far, far less than SetFramerateLimit().

So, take heed everyone -- take a look at your project and see if you are using this line, see what happens if you play with the parameter or take this call out of your code altogether.

Laurent has mentioned before that it's simply relying on the OS's sleep() method under the hood, and I'm guessing my flavour of Windows just likes to take a nice long nap ~ once per second!   : )

A complete & minimal example code:

Code: [Select]

#include <SFML/Window.hpp>

static void resize(int w, int h);
static void renderLoop(sf::Window & App);
static void drawSquare();

static const float ROTATE_DEGREES_PER_SEC = 45.0f;

int main()
{
    const int initialWidth = 1024;
    const int initialHeight = 768;

    sf::Window App(sf::VideoMode(initialWidth, initialHeight, 32), "SFML");

    // Try uncommenting *any* of these to cause the animation to
    // stutter on Win7:
    //// App.SetFramerateLimit(30);
    //// App.SetFramerateLimit(60);
    //// App.SetFramerateLimit(120);
    //// App.SetFramerateLimit(360);
    //// App.UseVerticalSync(true);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glShadeModel(GL_SMOOTH);

    resize(initialWidth, initialHeight);
    renderLoop(App);

    return EXIT_SUCCESS;
}

static void resize(int w, int h)
{
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(
        45.0f,                 // fov
        float(w) / float(h),   // aspect ratio
        1.0f,                  // near clip
        500.0f);               // far clip

    glMatrixMode(GL_MODELVIEW);
}

static void renderLoop(sf::Window & App)
{
    sf::Event Event;
    sf::Clock Clock;

    while (App.IsOpened())
    {
        while (App.GetEvent(Event))
        {
            if((Event.Type == sf::Event::Closed) ||
               (Event.Type == sf::Event::KeyPressed))
                App.Close();

            if (Event.Type == sf::Event::Resized)
                resize(Event.Size.Width, Event.Size.Height);
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glLoadIdentity();
        glTranslatef(0.0f, 0.0f, -3.0f);
        glRotatef(Clock.GetElapsedTime() * ROTATE_DEGREES_PER_SEC,
          0.0f, 0.0f, 1.0f);

        drawSquare();

        App.Display();
    }
}

static void drawSquare()
{
    glBegin(GL_QUADS);

        glColor3ub(255, 255, 0);        glVertex3f(-0.5f, -0.5f, 0.5f);
        glColor3ub(255, 0, 0);          glVertex3f( 0.5f, -0.5f, 0.5f);
        glColor3ub(255, 0, 0);          glVertex3f( 0.5f,  0.5f, 0.5f);
        glColor3ub(0, 255, 255);        glVertex3f(-0.5f,  0.5f, 0.5f);

    glEnd();
}



I hope the documentation can be updated to warn the user about the potential irregularities introduced by the timers used by SetFramerateLimit().  I see in another post that Laurent has decided to altogether remove GetFrameTime() from 2.0 anyway, for the same reasons.

Laurent:  thanks for all your work on SFML; it is great, and inspiring (even from the clean top-level design, not to mention all the gnarly OS-specific work going on underneath...), and I look forward to v2.0.  My only chore with it was to transfer the VS2008 project to VS2010 Express, download the Win7 SDK and figure out how to compile SFML against it, as the binaries did not work for me in Win7 as-is.  A sample Visual Studio 2010 project at least would be cool -- I can provide one for the community if you like.

-----------------------

Completely off topic, also:  could we add a very simple:

Code: [Select]
 bool sf::Window::IsFullscreen() const;  

predicate?  I'm having to keeping track of that state myself, in order to "toggle" fullscreen mode on/off, which seems kind of silly.  (since we cannot get access to the sf::Style after the window is created...)

Mediocritus

  • Newbie
  • *
  • Posts: 4
    • View Profile
Confirmed on Linux (to 2x the monitor refresh rate anyway)
« Reply #1 on: January 23, 2012, 04:10:25 pm »
I ran some trials on a Red Hat Linux box (64-bit) with GNOME, and a 60Hz monitor just to see if it still happens on other OS'es.  The effect is definitely still there when I specify 30-60-120Hz, but it gets very very subtle after 120Hz, i.e. the lines:

Code: [Select]

    App.SetFramerateLimit(128);
    // or:
    App.SetFramerateLimit(360);


are *almost* as good as not having any limit set -- but I can still see the speed of the rotation is slightly inconsistent/wobbly once in a while.  There is no noticeable "pausing" or stuttering at these rates, like on Windows though.

But without this line, the rotation of the square is just gorgeous, silky smooth, and it's still very possible to tell the difference if you run two windows side by side at the same time (one with a framerate limit, one without).

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
SetFramerateLimit() causes stutter & choppy animation
« Reply #2 on: January 23, 2012, 04:17:57 pm »
You should try SFML 2, the implementation of timing functions have changed and is supposed to be more reliable.

Quote
since we cannot get access to the sf::Style after the window is created...

It will probably be added in a future minor release of SFML 2.
Laurent Gomila - SFML developer

Mediocritus

  • Newbie
  • *
  • Posts: 4
    • View Profile
Confirmed with SFML 2 on Linux
« Reply #3 on: January 23, 2012, 05:59:35 pm »
OK, I have built the snapshot of SFML 2.0 from the front page of the website to try this out, using the same Linux Red Hat 64-bit OS.  And the results are:

...  the same!

If the implementation has changed I cannot tell the difference.   : (   It looks exactly the same as SFML 1.6 on my 60Hz monitor:

  * setting a limit of 30-32Hz looks *awful*,
  * setting a limit of 60-64Hz is also noticeably choppy, and
  * setting a limit of 120-360Hz is barely noticeable, but definiely
     inferior to no framerate limit at all, which looks wonderful.

I had to change a few things to get it to compile (include OpenGL.hpp, add "AsSeconds()" to extract the clock time as a float, change some function names...) but other than that, all the same results.  I build optimized ("Release" mode) SFML2, and linked statically to the above example, also compiled optimized.

I might try it on Windows as well when I get back home.

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
SetFramerateLimit() causes stutter & choppy animation
« Reply #4 on: January 23, 2012, 11:35:46 pm »
How do you animate your objects or whatever you're drawing? This sounds a bit like you're relying solely on the frame rate's accuracy to get smooth movement, which might result in some discrepancies (e.g. 59 vs. 61 fps with a 60 fps limit).

Mediocritus

  • Newbie
  • *
  • Posts: 4
    • View Profile
Very simply!
« Reply #5 on: January 23, 2012, 11:48:19 pm »
(Um...  I posted the full program code in my first post, above...!  That's all there is to it.  It's basically the SFML 1.6 tutorial almost verbatim; actually even simpler, just one side of the cube instead of all six sides, and only one rotation instead of three.  Feel free to copy-paste into a fresh main.cpp and try it at home.)

But specifically, on each frame I am rotating the square by an angle of SOME_CONSTANT * sf::Clock::GetElapsedTime().AsSeconds().

I am not "accumulating" the rotation angles (summation).

I am not using fps, or frame-to-frame intervals.

I am just using "current time" -- of course, if sf::Window::Display() calls sleep() before rendering the frame, that might explain this behavior.  Then the timestamp that I am using might not correspond very well to the time that the image ends up drawn on screen.

Otherwise, the orientation of the square should be independent of fps, and strictly a function of wall-clock time (OK ok, plus some uncontrollable little delay between the sf::Clock::GetElapsedTime() and the time the image ends up on the monitor, and the time that it takes for the light to travel to my eyes and for my brain to register it & process it).

Mario

  • SFML Team
  • Hero Member
  • *****
  • Posts: 879
    • View Profile
SetFramerateLimit() causes stutter & choppy animation
« Reply #6 on: January 26, 2012, 01:09:19 am »
I think OpenGL will wait before swapping buffers (i.e. before stuff that has been drawn will be shown; not after). That might indeed be the reason for this. I didn't check the source for the built in frame limit, but I guess it will wait before displaying content too (to adapt to the time it took till you reached the "display()" call.).

 

anything