Frame limiting

iamwhosiam wrote on Tuesday, June 10, 2014:

i don’t know where else to go with this question, but this following code successfully locked the frame rate in sdl.

when i ported it to glfw it no longer works. the frame rate it locks too is seemingly random. could someone pretty please take a look at this snippet and help me out?

double wait_time = 1.0 / (targetFrameRate);

if(glfwGetTime() - frame_start < wait_time)
{
    double dur = (wait_time - (glfwGetTime() - frame_start));
    Sleep((DWORD)(dur * 1000.0 + 0.5));
}

frame_start = glfwGetTime();

any help is much appreciated :slight_smile:

dougbinks wrote on Wednesday, June 11, 2014:

Where are you calling glfwSwapBuffers()? You should do this before you measure the frame_start time, otherwise you’re not taking into account the actual rendering time.

arampl wrote on Wednesday, June 11, 2014:

i’m not using sleep function:

void renderfps(int framerate) 
{ 
	currentTime = glfwGetTime(); 

	if(currentTime - lastTime >= 1.0 / framerate) 
	{
		lastTime = currentTime;
		render(); 
	}
}

currentTime, lastTime - doubles
render() is my rendering function with glfwSwapBuffers()

renderfps(60) - 60 fps, called in the main loop

iamwhosiam wrote on Wednesday, June 18, 2014:

arampl: i tried this technique and it worked but since the process never sleeps it takes up too much cpu and i dont want my game to be a hog :wink:

doug binks: here’s my main loop, i took out the non-relevant bits and i think the functions are pretty self explanatory.

while( !glfwWindowShouldClose(window) )
{
tw_time_frame_start(); // get the start time
handle_events();
update_func();

setup_2d_proj();

draw_func();

glfwSwapBuffers(window);

tw_time_update(); // actual sleep code is in here
tw_input_update();

}

dougbinks wrote on Wednesday, June 18, 2014:

@iamwhosiam: At brief glance the code seems reasonable, but this is hard to tell without all the code. I’d advise trying to create a small test app, and if that has problems you could share it with me and I could help debug it.

I would try a few changes.

Make sure you time the entire loop, so the start time should be measured after the tw_time_update(). This way if you have any time being taken up inside tw_input_update this is being accounted for.

Write your code for easy debugging, and make sure you don’t pass 0 to Sleep. Example below:

double wait_time = 1.0 / (targetFrameRate);
double curr_frame_time = glfwGetTime() - frame_start;
double dur = 1000.0 * ( wait_time - curr_frame_time ) + 0.5;
DWORD durDW = (DWORD)dur;
if( durDW > 0 ) // ensures that we don't have a dur > 0.0 which converts to a durDW of 0.
{
    Sleep( durDW  );
}

double frame_end = glfwGetTime();
// here you could print to file or OutputDebugStream curr_frame_time, frame_end - frame_start and dur.
frame_start = frame_end;

Note that there is no guarantee that Sleep will return exactly on the time you require. It’s possible your odd frame times were due to passing in a 0 value to Sleep, which causes the thread to give up it’s time slice.

dougbinks wrote on Wednesday, June 18, 2014:

Oops - forgot DWORD is unsigned. Make sure to use a signed integer type when converting from double as it’s undefined as to what happens for negative values on cast to unsigned int.

iamwhosiam wrote on Friday, June 20, 2014:

ok i made a test app that just creates a window then just calculates frame rate and sleeps and it’s still doing it.

ive uploaded it to http://pastebin.com/E1kGThRn

one question i have, is should i be setting the swap interval to zero or maybe one? or just leave it default? having vsync on would be cool but im not sure if it would mess with the frame rate. also some peoples computers might just force it off anyway so, just leave it default and make em both work?

dougbinks wrote on Friday, June 20, 2014:

This code works for - I can change the target_frame_rate and never get more, though can get less obviously.

Note that glfwSetWindowTitle can be expensive, so I moved this into calc_frame_rate inside the if(elapsed>1) so as to change the title only once per second.

Preferably you should use something like FRAPS or gDEBugger to check frame rate, and this shows a nice solid flat FPS graph for me.

iamwhosiam wrote on Friday, June 20, 2014:

ok, thank u very much for your time :slight_smile:

dougbinks wrote on Friday, June 20, 2014:

On the question of vsync, I would make that user configurable. I prefer having it on, but some don’t. Also note the documentation on glfwSwapInterval and negative swap intervals which can work well.

Note on Windows the _GLFW_USE_DWM_SWAP_INTERVAL definition (requires a recompile of glfw) controls whether to ignore swap interval settings when the DWM compositing is on, and as you say many drivers can override the application settings.