C ++ SDL frame rate pulsating

I recently worked on some SDL shells for my own use, and I had a problem that I have encountered since I started working with the SDL library.

See, like many others, I used a timer similar to this http://lazyfoo.net/SDL_tutorials/lesson14/index.php to adjust the frame rate, and the movement is never smooth; smooth. This does not look like double buffering or a vsync problem, rather the movement is smooth, but occasionally skips and stutters (in fact, it has a certain pulse and rhythm). Whenever the frame rate control is turned off, the pulse disappears - and, of course, everything becomes unusable), so I'm basically sure that it has something to do with it.

I put together a small application consisting only of a timer and a red square moving around it. I will put the code at the end of this post. The code is garbage and does not use any sprite or anything else: just know that it stutters the same way no matter how much or a little stress I put into it (say, add more moving things or just refresh part of the screen). I tried with different settings for the frame rate, and the pulse always appears (only with a different "pace"). Needless to say, I tried this on multiple machines (all linux machines).

Anyway, I read and saw other people having this problem, but no real answer. For example, I will talk about the methods of delta synchronization and try it with pleasure, but my problem here is with the timer itself in this very simple application. Why does this stutter? Is this a problem with the accuracy and precision of SDL_GetTicks ?. What can I do to improve it?

So, here is the code for those who want to try:

#include <SDL/SDL.h> class fps_control { private: bool apply; Uint32 ticks_frame_count; Uint32 ticks_frame_end; Uint32 ticks_frame_begin; Uint32 diff; unsigned int frame_count; //Visible to the outside unsigned int frame_count_inner; //Keeps count until a second has passed, then overwrites former to give the elapsed frames in a second. unsigned int frame_length; unsigned int desired_framerate; private: void calculate_frame_length() { this->frame_length=1000 / this->desired_framerate; } public: fps_control(unsigned int p_f=30):desired_framerate(p_f), apply(true), ticks_frame_count(0), ticks_frame_end(0), ticks_frame_begin(0), diff(0), frame_count(0), frame_count_inner(0), frame_length(0) { this->calculate_frame_length(); } unsigned int get_frame_count() const {return this->frame_count;} unsigned int get_desired_framerate() const {return this->desired_framerate;} void framerate_increase(){this->set_framerate(this->desired_framerate+1);} void framerate_decrease(){this->set_framerate(this->desired_framerate+1);} void set_framerate(unsigned int p_param) { this->desired_framerate=p_param; this->calculate_frame_length(); } void toggle_apply() {this->apply=!this->apply;} void init() { //Call this once before starting, to set the beginning frame count to the initial values. this->ticks_frame_count=SDL_GetTicks(); this->ticks_frame_begin=this->ticks_frame_count; this->frame_count_inner=0; this->frame_count=0; } void turn() { //Call this when all drawing and logic is done. if(!this->apply) return; //Only apply when asked for. //Ask for time before drawing and logic to calculate the difference. Add a frame. this->ticks_frame_end=SDL_GetTicks(); this->frame_count_inner++; //Whenever a second has passed, update the visible frame count. if( (this->ticks_frame_end - this->ticks_frame_count) > 1000) { this->frame_count=this->frame_count_inner; this->frame_count_inner=0; this->ticks_frame_count=SDL_GetTicks(); } //Calculate difference and apply delay when needed. this->diff=this->ticks_frame_end - this->ticks_frame_begin; if(this->diff < this->frame_length) SDL_Delay(this->frame_length-this->diff); //Get the beginning time and start again. this->ticks_frame_begin=SDL_GetTicks(); } }; class Box { private: SDL_Rect position; int movement_x; int movement_y; public: Box():movement_x(4), movement_y(4) { this->position.x=100; this->position.y=100; this->position.w=30; this->position.h=30; } SDL_Rect get_position() {return this->position;} void move_around() { //Won't touch the edges, but doesn't really matter. if(this->position.x<=0 || this->position.x>=800-this->position.w) this->movement_x=-this->movement_x; if(this->position.y<=0 || this->position.y>=600-this->position.h) this->movement_y=-this->movement_y; this->position.x+=this->movement_x; this->position.y+=this->movement_y; } }; bool init_sdl(){return SDL_Init( SDL_INIT_VIDEO ) >= 0;} void quit_sdl(){SDL_Quit();} void fill_screen(SDL_Surface * screen) { SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,0,0,0)); } void update_screen(SDL_Surface * screen, Box& box) { SDL_Rect b=box.get_position(); SDL_FillRect(screen, &b, SDL_MapRGB(screen->format, 200, 20, 20)); SDL_Flip(screen); } int get_input() { SDL_Event event; while(SDL_PollEvent(&event)) { if(event.type==SDL_QUIT) return 1; else if(event.type==SDL_KEYDOWN) { switch(event.key.keysym.sym) { case SDLK_ESCAPE: return 1; break; case SDLK_SPACE: return 2; break; case SDLK_UP: return 3; break; case SDLK_DOWN: return 4; break; default: break; } } } return 0; } int main(int argc, char **argv) { if(!init_sdl()) { return 1; } else { //Init things... // SDL_Surface * screen=SDL_SetVideoMode(800, 600, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); /*SDL_SWSURFACE | SDL_ANYFORMAT);*/ SDL_Surface * screen=SDL_SetVideoMode(800, 600, 16, SDL_SWSURFACE | SDL_ANYFORMAT); Box box=Box(); fps_control fps=fps_control(60); //Framerate is set to 60. bool run=true; int input=0; SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format,255,0,0)); //Main loop fps.init(); while(run) { input=get_input(); switch(input) { case 1: run=false; break; case 2: fps.toggle_apply(); break; case 3: fps.framerate_increase(); break; case 4: fps.framerate_decrease(); break; default: break; } box.move_around(); fill_screen(screen); update_screen(screen, box); fps.turn(); } quit_sdl(); } } 

This is self-sufficient (and again, clean garbage), so just link it to the SDL and try ... Do you see any stuttering pulse here ?.

I will try to use the delta timer to find out if the problem has disappeared. I just want to know why this happens in such simple applications. Thank you very much.

Edit: last, I know that I probably shouldn't go with SDL_Delay at all (something about the system sleeping for at least a given value), but I'm really puzzled by this behavior of seemingly powerful machines.

Edit: a bit of "set_framerate" fixed, thanks to Yno for commenting on it.

Edit 2: Since I changed all the code to work with the delta time value, and not with the set frame rate, everything went much better. I still get some periodic slowdowns (this time every forty seconds), but I can blame them on the system, as the frame rate and slowdown vary greatly depending on the Linux desktop used (say Gnome, Unity, Gnome2d ... )

+4
source share
2 answers

Your fps_control class simply does not do this work, it is completely buggy. The framerate_decrease() function is framerate_decrease() :

 void framerate_decrease(){this->set_framerate(this->desired_framerate+1);} 

It should be -1 . calculate_frame_length() should be called every time your desired FPS changes, i.e. to set_framerate() :

 void set_framerate (unsigned int p_param) { desired_framerate = p_param; calculate_frame_length (); } 

Also be careful with your division in calculcate_frame_length() , you can split by zero:

 void framerate_decrease() { if (desired_framerate > 1) set_framerate (desired_framerate - 1); } 

Beyond these issues, your class looks uselessly complex. If you want to limit the frame rate, you must understand how it is calculated. The usual way to do this is to calculate the time your frames need using SDL_GetTicks() :

 int t; while (run) { t = SDL_GetTicks (); // ... t = SDL_GetTicks () - t; } 

At the end of the loop, t is the number of ms that your frame took to complete. 1000 / t is your frames per second. If your frame rate is too high (i.e., T is too low), you must fill in the gap between the current frame time and the desired frame time by calling SDL_Delay() . Let's say FPS is your FPS limit, the time each frame should take is 1000 / FPS .

 int t; while (run) { t = SDL_GetTicks (); // ... t = SDL_GetTicks () - t; // if the framerate is too high if (t < 1000 / FPS) { // compute the difference to have a total frame time of 1000 / FPS SDL_Delay ((1000 / FPS) - t); } } 

Hope this helps.

+4
source

I don’t know what is with your SDL code, but if it is used, I just wrote a GLFW-using class FpsManager, which limits the upper value and provides temporary deltas to maintain constant motion regardless of the frame rate. The principles will be identical.

This is not the best code, but it is very well documented and completely smoothed between 10fps and 1000fps when changing the target FPS +/- by pressing a key.

If this is useful to you, you can find it at: FpsManager - C ++ helper class for independent frame rate changes

+3
source

All Articles