18

I'm trying to make an SDL program that runs with a constant frame rate. However I'm finding that even though my program is lagging a lot and skipping a lot of frames (even though it's running at a low frame and isn't rendering much).

Do you guys have any suggestions to make my program run smoother?

#include "SDL.h"
#include "SDL/SDL_ttf.h"

//in milliseconds
const int FPS = 24;
const int SCREENW = 400;
const int SCREENH = 300;
const int BPP = 32;

void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination) {

    SDL_Rect offset;

    offset.x = x;

    offset.y = y;



    if(SDL_BlitSurface(source, NULL, destination, &offset) < 0) {
        printf("%s\n", SDL_GetError());
    }

}

int main(int argc, char* argv[]) {
    //calculate the period
    double period = 1.0 / (double)FPS;
    period = period * 1000;
    int milliPeriod = (int)period;
    int sleep;

    SDL_Init(SDL_INIT_EVERYTHING);
    TTF_Init();

    TTF_Font* font = TTF_OpenFont("/usr/share/fonts/truetype/freefont/FreeMono.ttf", 24);
    SDL_Color textColor = { 0x00, 0x00, 0x00 };

    SDL_Surface* screen = SDL_SetVideoMode(SCREENW, SCREENH, BPP, SDL_SWSURFACE);
    SDL_Surface* message = NULL;

    Uint32 white = SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF);

    SDL_Event event;

    char str[15];

    Uint32 lastTick;
    Uint32 currentTick;
    while(1) {
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) {
                return 0;
            }
            else {
                lastTick = SDL_GetTicks();

                sprintf(str, "%d", lastTick);
                message = TTF_RenderText_Solid(font, str, textColor);
                if(message == NULL) { printf("%s\n", SDL_GetError()); return 1; }

                //the actual blitting
                SDL_FillRect(screen, &screen->clip_rect, white);
                apply_surface(SCREENW / 2, SCREENH / 2, message, screen);

                currentTick = SDL_GetTicks();

                //wait the appropriate amount of time
                sleep = milliPeriod - (currentTick - lastTick);
                if(sleep < 0) { sleep = 0; }
                SDL_Delay(sleep);

                SDL_Flip(screen);
            }
        }
    }

    TTF_CloseFont(font);
    TTF_Quit();
    SDL_Quit();

    return 0;
}
SDLFunTimes
  • 745
  • 2
  • 7
  • 13

4 Answers4

10

There is a small example of how to do this at http://www.libsdl.org/release/SDL-1.2.15/docs/html/guidetimeexamples.html:

#define TICK_INTERVAL    30

static Uint32 next_time;

Uint32 time_left(void)
{
    Uint32 now;

    now = SDL_GetTicks();
    if(next_time <= now)
        return 0;
    else
        return next_time - now;
}


/* main game loop */

    next_time = SDL_GetTicks() + TICK_INTERVAL;
    while ( game_running ) {
        update_game_state();
        SDL_Delay(time_left());
        next_time += TICK_INTERVAL;
    }
CraftedCart
  • 651
  • 7
  • 16
SamB
  • 9,039
  • 5
  • 49
  • 56
  • @Jjpx: not anymore, but I guess that was a link-only answer, so I guess I'll paste it here ... – SamB Nov 27 '14 at 22:05
9

Don't sleep.

Instead, use a linear interpolation function to compute your position given the current time each time through the main loop. Doing this will guarantee that no matter the hardware, space ships arrive at their destinations at the same time (though on a fast machine, you'll see more of the steps in between).

Also there are other interpolation/blending functions (like linear ease in, ease out, quadratic ease in/out, cubic, etc.) for other cool effects.

Check this link out: Frame Rate Independent Linear Interpolation

Michaelangel007
  • 2,798
  • 1
  • 25
  • 23
dicroce
  • 45,396
  • 28
  • 101
  • 140
  • 23
    I think it might be a good idea to sleep to save batteries. Interpolation is good and in some games it's good to do it anyway, but if my game can achieve 60FPS while using, say 20% of the CPU, there is ABSOLUTELY no need to run any faster, since the display is most probably running at 60Hz anyway, so I'm just wasting battery life on notebooks and devices. – Tomas Andrle Sep 09 '11 at 11:54
  • @TomA Doesn't SDL do VSync which caps the framerate anyways? – Jonathan Baldwin Jan 11 '14 at 03:18
  • You can chose between 3 types of V-Sync with SDL 2.x. See SDL_GL_SetSwapInterval() – Michaelangel007 Mar 14 '14 at 19:47
  • 1
    "Doing this will guarantee that no matter the hardware, space ships arrive at their destinations at the same time" - this would be true if floating point computations had infinite resolution. The fact is that things will move at slightly (or not so slightly - it depends) different speeds at different frame rates. In floating point math, `0.1 * 10 != 10`. This technique might be useful, but handle it with care. – jacwah Jul 15 '15 at 13:40
4

As dicroce said, don't sleep. Why? Because most desktop operating systems are not hard real-time systems, and therefore sleep(10) does not mean "wake me up in exactly 10 milliseconds", it means "ensure I am asleep for at least 10 milliseconds". This means that sometimes you spend a lot longer sleeping than you wanted. This is rarely what you want. If smooth gameplay is important then generally you just want to render as frequently as you can - SDL_Flip handles that for you, providing your video drivers don't have VSync disabled.

Instead of sleeping, dicroce suggested a linear interpolation algorithm to calculate the correct positions for your entities at any given time. While this is a reasonable strategy for many games, and one I usually use myself, it can cause problems in some cases if not handled carefully: the "Integration Basics" article explains some of this. An alternative offered in the same author's next article, called "Fix Your Timestep".

Kylotan
  • 18,290
  • 7
  • 46
  • 74
0

The approach I've tried and seems working is one without delays but using something along the lines:

int desired_fps = 60; 
int last_ticks = SDL_GetTicks();
while (true) {
     if (SDL_GetTicks() - last_ticks < 1000/desired_fps) {
         continue;
     } 
     last_ticks = SDL_GetTicks();
     // ... render frame...
}