Programming Trivia

Framerate Throttling

Can you spot the timing error in this code? It doesn't matter, but the language is D. Though it doesn't affect the bug I found (there are bugs I haven't found), SDL_Delay sleeps the thread for the given number of milliseconds.

void main ()
{
	const int desired_framerate = 1; // frames per second
	const int milliseconds_per_frame = 1000 / desired_framerate;

	// Track the starting time
	int last_ticks = SDL_GetTicks ();

	main_loop:
	while ( running )
	{
		// Calculate time spent on last frame
		int current_ticks = SDL_GetTicks ();
		int frame_duration = current_ticks - last_ticks;
		last_ticks = current_ticks;

		// Update the game
		running = update ( frame_duration );
		
		if ( running )
		{
			// Render current game state to the screen
			game_render ();

			// Cap the framerate
			if ( frame_duration < milliseconds_per_frame )
			{
				writefln ( "Waiting %d", milliseconds_per_frame - frame_duration );
				SDL_Delay ( milliseconds_per_frame - frame_duration );
			}
		}
	}
}

My Answer

Here is a corrected version which seems to fix the timing error, but does not fix many bugs I don't know about.

void main ()
{
	const int desired_framerate = 1; // frames per second
	const int milliseconds_per_frame = 1000 / desired_framerate;

	// Track the starting time
	int last_ticks = SDL_GetTicks ();

	main_loop:
	while ( running )
	{
		// Calculate time spent on last frame
		int current_ticks = SDL_GetTicks ();
		int frame_duration = current_ticks - last_ticks;

		// Cap the framerate
		if ( frame_duration < milliseconds_per_frame )
		{
			writefln ( "Waiting %d", milliseconds_per_frame - frame_duration );
			SDL_Delay ( milliseconds_per_frame - frame_duration );

			current_ticks = SDL_GetTicks ();
			frame_duration = current_ticks - last_ticks;
		}

		last_ticks = current_ticks;

		// Update the game
		running = update ( frame_duration );
		
		if ( running )
		{
			// Render current game state to the screen
			game_render ();
		}
	}
}

I found the problem by analyzing the output from two writefln function calls. The first is listed in the "Cap the framerate" block, and another is inside the update function, and prints frame_duration. I noticed a pattern like this:

Waiting 17
Update 31
Render!
Waiting 969
Update 984
Render!
Waiting 16
Update 32
Render!
Waiting 968
Update 980
Render!

Apparently, the time spent by SDL_Delay was being counted toward the the duration of the next frame. So one frame would be throttled properly, but the time spent in SDL_Delay was counted toward the next frame's time. Thus, it would appear to have spent about exactly the right amount of time, and there would be a very short delay. Then the next frame would be seen as taking a very short amount of time, so the delay would be longer, although close to being accurate.

Proper output looks like this:

Waiting 1000
Update 1000
Render!
Waiting 987
Update 1000
Render!
Waiting 988
Update 1000
Render!
Waiting 984

By the way, this is all assuming that the update and game_render functions take much less time than one frame. If they take longer, the SDL_Delay call won't be made at all.

While ensuring that the SDL_Delay call wouldn't be made if the frame rendered fast enough, I noticed that when the delay is added, the time spent per frame (total frame time − time passed to SDL_Delay) averages about 12 milliseconds on my computer. When the delay block is not activated, the frame takes about 8 milliseconds to render. That means that between SDL_Delay, SDL_GetTicks, writefln, and the integer arithmetic, there are a few milliseconds being lost when the game is moving too quickly. So far, for me, this is not a problem worth worrying about, but it's good to know.

Prime number sieve

This is another cool problem: generating prime numbers. I made my prime number sieve multi-threaded in the best way I knew how, but got zero speed-up on a dual-core system. Why?


Comments

Click here to view the comments on this post.