11

I am currently working on an OpenGL application to display a few 3D spheres to the user, which they can rotate, move around, etc. That being said, there's not much in the way of complexity here, so the application runs at quite a high framerate (~500 FPS).

Obviously, this is overkill - even 120 would be more then enough, but my issue is that running the application at full-stat eats away my CPU, causing excess heat, power consumption, etc. What I want to do is be able to let the user set an FPS cap so that the CPU isn't being overly used when it doesn't need to be.

I'm working with freeglut and C++, and have already set up the animations/event handling to use timers (using the glutTimerFunc). The glutTimerFunc, however, only allows an integer amount of milliseconds to be set - so if I want 120 FPS, the closest I can get is (int)1000/120 = 8 ms resolution, which equates to 125 FPS (I know it's a neglegible amount, but I still just want to put in an FPS limit and get exactly that FPS if I know the system can render faster).

Furthermore, using glutTimerFunc to limit the FPS never works consistently. Let's say I cap my application to 100 FPS, it usually never goes higher then 90-95 FPS. Again, I've tried to work out the time difference between rendering/calculations, but then it always overshoots the limit by 5-10 FPS (timer resolution possibly).

I suppose the best comparison here would be a game (e.g. Half Life 2) - you set your FPS cap, and it always hits that exact amount. I know I could measure the time deltas before and after I render each frame and then loop until I need to draw the next one, but this doesn't solve my 100% CPU usage issue, nor does it solve the timing resolution issue.

Is there any way I can implement an effective, cross-platform, variable frame rate limiter/cap in my application? Or, in another way, is there any cross-platform (and open source) library that implements high resolution timers and sleep functions?

Edit: I would prefer to find a solution that doesn't rely on the end user enabling VSync, as I am going to let them specify the FPS cap.

Edit #2: To all who recommend SDL (which I did end up porting my application to SDL), is there any difference between using the glutTimerFunc function to trigger a draw, or using SDL_Delay to wait between draws? The documentation for each does mention the same caveats, but I wasn't sure if one was more or less efficient then the other.

Edit #3: Basically, I'm trying to figure out if there is a (simple way) to implement an accurate FPS limiter in my application (again, like Half Life 2). If this is not possible, I will most likely switch to SDL (makes more sense to me to use a delay function rather then use glutTimerFunc to call back the rendering function every x milliseconds).

Breakthrough
  • 2,444
  • 2
  • 23
  • 37
  • See related question: [What's the usual way of controlling frame rate?](http://stackoverflow.com/questions/5508922/whats-the-usual-way-of-controlling-frame-rate). – André Caron Jul 15 '11 at 14:29

6 Answers6

4

I think a good way to achieve this, no matter what graphics library you use, is to have a single clock measurement in the gameloop to take every single tick (ms) into account. That way the average fps will be exactly the limit just like in Half-Life 2. Hopefully the following code snippet will explain what I am talking about:

//FPS limit
unsigned int FPS = 120;

//double holding clocktime on last measurement
double clock = 0;

while (cont) {
    //double holding difference between clocktimes
    double deltaticks;

    //double holding the clocktime in this new frame
    double newclock;

    //do stuff, update stuff, render stuff...

    //measure clocktime of this frame
    //this function can be replaced by any function returning the time in ms
    //for example clock() from <time.h>
    newclock = SDL_GetTicks();

    //calculate clockticks missing until the next loop should be
    //done to achieve an avg framerate of FPS 
    // 1000 / 120 makes 8.333... ticks per frame
    deltaticks = 1000 / FPS - (newclock - clock);

    /* if there is an integral number of ticks missing then wait the
    remaining time
    SDL_Delay takes an integer of ms to delay the program like most delay
    functions do and can be replaced by any delay function */
    if (floor(deltaticks) > 0)
        SDL_Delay(deltaticks);

    //the clock measurement is now shifted forward in time by the amount
    //SDL_Delay waited and the fractional part that was not considered yet
    //aka deltaticks
    the fractional part is considered in the next frame
    if (deltaticks < -30) {
        /*dont try to compensate more than 30ms(a few frames) behind the
        framerate
        //when the limit is higher than the possible avg fps deltaticks
        would keep sinking without this 30ms limitation
        this ensures the fps even if the real possible fps is
        macroscopically inconsitent.*/
        clock = newclock - 30;
    } else {
        clock = newclock + deltaticks;
    }

    /* deltaticks can be negative when a frame took longer than it should
    have or the measured time the frame took was zero
    the next frame then won't be delayed by so long to compensate for the
    previous frame taking longer. */


    //do some more stuff, swap buffers for example:
    SDL_RendererPresent(renderer); //this is SDLs swap buffers function
}

I hope this example with SDL helps. It is important to measure the time only once per frame so every frame is taken into account. I recommend to modularize this timing in a function which also makes your code clearer. This code snipped has no comments in the case they just annoyed you in the last one:

unsigned int FPS = 120;

void renderPresent(SDL_Renderer * renderer) {
    static double clock = 0;
    double deltaticks;
    double newclock = SDL_GetTicks();

    deltaticks = 1000.0 / FPS - (newclock - clock);

    if (floor(deltaticks) > 0)
        SDL_Delay(deltaticks);

    if (deltaticks < -30) {
        clock = newclock - 30;
    } else {
        clock = newclock + deltaticks;
    }

    SDL_RenderPresent(renderer);
}

Now you can call this function in your mainloop instead of your swapBuffer function (SDL_RenderPresent(renderer) in SDL). In SDL you'd have to make sure the SDL_RENDERER_PRESENTVSYNC flag is turned off. This function relies on the global variable FPS but you can think of other ways of storing it. I just put the whole thing in my library's namespace.


This method of capping the framerate delivers exactly the desired average framerate if there are no large differences in the looptime over multiple frames because of the 30ms limit to deltaticks. The deltaticks limit is required. When the FPS limit is higher than the actual framerate deltaticks will drop indefinitely. Also when the framerate then rises above the FPS limit again the code would try to compensate the lost time by rendering every frame immediately resulting in a huge framerate until deltaticks rises back to zero. You can modify the 30ms to fit your needs, it is just an estimate by me. I did a couple of benchmarks with Fraps. It works with every imaginable framerate really and delivers beautiful results from what I have tested.


I must admit I coded this just yesterday so it is not unlikely to have some kind of bug. I know this question was asked 5 years ago but the given answers did not statify me. Also feel free to edit this post as it is my very first one and probably flawed.

EDIT: It has been brought to my attention that SDL_Delay is very very inaccurate on some systems. I heard a case where it delayed by far too much on android. This means my code might not be portable to all your desired systems.

neop
  • 89
  • 2
  • 9
4

I'd advise you to use SDL. I personnally use it to manage my timers. Moreover, it can limit your fps to your screen refresh rate (V-Sync) with SDL 1.3. That enables you to limit CPU usage while having the best screen performance (even if you had more frames, they wouldn't be able to be displayed since your screen doesn't refresh fast enough).

The function is

SDL_GL_SetSwapInterval(1);

If you want some code for timers using SDL, you can see that here :

my timer class

Good luck :)

Tuxer
  • 41
  • 1
  • Does `SDL_GL_SetSwapInterval(1);` cause the FPS to be capped to my refresh rate even if I explicitly disable it in my graphics card driver? (e.g. in my NVIDIA Control Panel, I can set Vertical Sync to "Force Off"). – Breakthrough Jul 15 '11 at 13:36
  • 1
    to answer one of your other questions, you can't really control time in a game in a better resolution than the millisecond. The CPU interruption systems don't permit you to do that, since maybe other applications will take CPU time as well. Milliseconds are enough :) I have no idea about your VSync force off, I use ATI. – Tuxer Jul 15 '11 at 14:08
3

The easiest way to solve it is to enable Vsync. That's what I do in most games to prevent my laptop from getting too hot. As long as you make sure the speed of your rendering path is not connected to the other logic, this should be fine.

There is a function glutGet( GLUT_ELAPSED_TIME ) which returns the time since started in miliseconds, but that's likely still not fast enough.

A simple way is to make your own timer method, which uses the HighPerformanceQueryTimer on windows, and the getTimeOfDay for POSIX systems.

Or you can always use timer functions from SDL or SFML, which do basically the same as above.

Marking
  • 754
  • 1
  • 8
  • 23
  • Thank you for the response, but I should also have mentioned that I wanted to implement a solution that did not require a user to enable VSync. As for the high performance timers, how would I go about making the application sleep before I tell it to draw the next frame? (I know how to do it on a millisecond resolution, but was hoping for something better) – Breakthrough Jul 15 '11 at 13:38
  • 1
    @Breakthrough: resolution of sleep operations is never guaranteed. Ask to sleep for smaller (safer) intervals and use the counter to find out exactly how much time you sleep *a posteriori*. Then, sleep if any time is left. – André Caron Jul 15 '11 at 14:30
  • Individual applications can still use vsync via swapinterval settings without requiring the user to enable it in the graphics driver. Even if you want to limit framerate to a lower amount (such as a video playback application), you should still use vsync to avoid the image tearing. –  Jul 15 '11 at 14:36
3

You should not try to limit the rendering rate manually, but synchronize with the display vertical refresh. This is done by enabling V sync in the graphics driver settings. Apart from preventing (your) programs from rendering at to high rate, it also increases picture quality by avoiding tearing.

The swap interval extensions allow your application to fine tune the V sync behaviour. But in most cases just enabling V sync in the driver and letting the buffer swap block until sync suffices.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • 5
    I just have to point out that vsync does add some lag, especially because of all the crap that graphics card drivers do. For example nvidia by default does 3 prerendered frames when vsync is on and with 60fps that's already 50ms. So my advice to game devs is to always give user 3 options: 1) vsync 2) unlimited fps 3) manual fps limit – Timo Jul 15 '11 at 15:35
  • @Timo: Seriously? I never ran into such an issue. On all my systems and all my graphics cards, and their drivers, having V sync enabled made my programs run at display refresh frequency. So please either provide some backing information. – datenwolf Jul 15 '11 at 15:46
  • 1
    @Timo, you are correct, I remember that issue now. There was a [noticeable lag between my input and the response](http://forums.anandtech.com/showthread.php?t=164868) on the screen when I had enabled VSync, so I have disabled it ever since. In my particular case here, though, a bit of lag would be acceptable (it's not a game) - although I would preferably like to give the user an option between the three, exactly as you said. @datenwolf, do you know how if I can set the swap interval using freeglut? If not, then I might have to go SDL... – Breakthrough Jul 15 '11 at 16:26
  • @Breakthrough: You can use the *swap control* extensions for this: http://www.opengl.org/registry/specs/EXT/swap_control.txt and http://www.opengl.org/registry/specs/EXT/wgl_swap_control.txt – however the most drivers allow to override any settings chosen by the application, so you can force V sync on or off, even if the application chooses different. – datenwolf Jul 15 '11 at 17:40
  • @AscensionSystems: VSync means, that you're synced to the monitor refresh frequency. This frequency may vary between 50Hz to 200Hz, depending on monitor and graphics card. Also VSync IS NOT meant for timing, but to prevent your program from rendering more frames than neccessary. To acurately time your animation, measure how long each frame rendering takes, then advance the animation by that. – datenwolf Nov 15 '11 at 20:17
  • 1
    @datenwolf yeah I dunno I musta been having a seizure when I was reading this and writing that comment. Gonna delete it lol. –  Nov 15 '11 at 20:42
  • 1
    And I just remembered that the refresh may become as low as 24Hz, e.g. movieplayer playing a 24Hz movie to a monitor that supports 24Hz, so everything stays in the original framerate without conversion. And I also once played with a system that could yield 300H (used with shutter glasses stereoscopy). – datenwolf Nov 15 '11 at 20:55
0

I would suggest using sub-ms precision system timers (QueryPerformanceCounter, gettimeofday) to get timing data. These can help you profile performance in optimized release builds also.

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • In retrospect, this is what I was trying to hint at. I was aware of using vertical synchronization, but what I was ultimately aiming at would infact require the use of sub-millisecond, high-performance timers. I infact like to have my application run independent of the vertical synchronization frequency, so others may want to consider the following question: [C++ Cross-Platform High-Resolution Timer](http://stackoverflow.com/questions/1487695/c-cross-platform-high-resolution-timer) – Breakthrough Jun 10 '13 at 17:34
  • Vsync is good, but in my experience it has been difficult to ensure it works properly. There seem to be many methods to override any of the API settings your program might use to set Vsync, and it would go unheeded. There is also some fancy new stuff supported on new hardware, called Frame Rate Target or other such names, which is basically Vsync without the frame-rate hit when dipping below the refresh rate. It's **very** nice because it is power efficient and not degrading to performance. That said, use of a high precision timer as a fallback to cap the framerate is a good idea. – Steven Lu Jun 10 '13 at 18:10
0

Some background information:

  • SDL_Delay is pretty much the same as Sleep/sleep/usleep/nanosleep but it is limited to milliseconds as parameter
  • Sleeping works by relying on the systems thread scheduler to continue your code.
  • Depending on your OS and hardware the scheduler may have a lower tick frequency than 1000hz, which results in longer timespans than you have specified when calling sleep, so you have no guarantee to get the desired sleep time.
  • You can try to change the scheduler's frequency. On Windows you can do it by calling timeBeginPeriod(). For linux systems checkout this answer.
  • Even if your OS supports a scheduler frequency of 1000hz your hardware may not, but most modern hardware does.
  • Even if your scheduler's frequency is at 1000hz sleep may take longer if the system is busy with higher priority processes, but this should not happen if your system isn't under super high load.

To sum up, you may sleep for microseconds on some tickless linux kernels, but if you are interested in a cross platform solution you should try to get the scheduler frequency up to 1000hz to ensure the sleeps are accurate in most of the cases.

To solve the rounding issue for 120FPS:

1000/120      = 8,333ms
(int)1000/120 = 8ms

Either you do a busy wait for 333 microseconds and sleep for 8ms afterwards. Which costs some CPU time but is super accurate. Or you follow Neop approach by sleeping sometimes 8ms and sometimes 9ms seconds to average out at 8,333ms. Which is way more efficent but less accurate.

Crigges
  • 1,083
  • 2
  • 15
  • 28