Sign in to follow this  
jsepia

Variable FPS in single threaded game?

Recommended Posts

My game currently detects the number of cores. If it's running on two or more cores, then it spawns a game thread that is meant to run at a constant 15 fps, and a gfx thread which just renders anytime the game is not busy (which usually results in 120-200 fps). I want to do a single threaded version too, to take advantage of single core processors when detected, but I want to keep the frame rate as high as possible (the animations look awful at 15 fps) and the game speed as constant as possible. I tried this:
void GameThread::RunSingleThreaded()
{
	int i;

	// these control how often the game thread updates
	long timeSinceLastUpdate;
	long lastUpdated = 1;

	// these control the amount of time passed between renders
	// (this is important for the particle system)
	long timeSinceLastRender;
	long lastRendered = 0;

	while(running)
	{
		// GET INPUT
		if(EscIsPressed()) {
			running = false;
			break;
		}

		// UPDATE GAME
		// This is supposed to run 15 times per second
		timeSinceLastUpdate = clock() - lastUpdated;
		if(timeSinceLastUpdate >= 1/15) {
			UpdateGame();
			lastUpdated = clock();
		}

		// UPDATE GRAPHICS
		// this is supposed to run everytime it can
		timeSinceLastRender = clock() - lastRendered;
		Render(timeSinceLastRender);
		lastRendered = clock();
	}
}

I get between 100 and 200 fps as normal, and the game's speed is unaffected, but this might be because I'm on a decent Athlon X2 4400+ with a good gfx card. But my target audience may have Pentium 4s and Semprons. With the above code, if the rendering ever approaches something like 22 fps (1 game update for every 1,5 cycles), the game's speed will be very irregular, and I don't want that. I need to keep the game speed constant. Is there a more elegant way to do this?

Share this post


Link to post
Share on other sites
Looking at what you've got so far, the most straightforward method that I can think of is just enclosing the Render() call in an if block to compare the amount of time left in this update tick ( clock() - lastUpdated ) with the amount of time rendering typically takes (taking the time it took for the last render call is probably a good enough guess, though you could go with a running average of the last n frames if you prefer).

One thing to be careful of doing that, though, is making sure your updates don't completely drown the renderer, so add an || to that if, compare the time since the last render call with some minimum target render frequency, a framerate low at which you're alright with introducing a little simulation lag.


float updateFrequency = 1/15;
float minRenderFrequency = 1/10;
if ( clock() - lastUpdated + averageTimePerRender < updateFrequency
|| timeSinceLastRender > minRenderFrequency
)
{
//Render in here
}


This isn't the most elegant way to tackle the problem, but I'm posting while I wait for a compile at work, so time's a little short :-P. I'll check back in again later tonight.

HTH

[Edited by - aberghage on August 18, 2009 3:57:09 PM]

Share this post


Link to post
Share on other sites
[quote]Original post by aberghage
Looking at what you've got so far, the most straightforward method that I can think of is just enclosing the Render() call in an if block to compare the amount of time left in this update tick ( clock() - lastUpdated ) with the amount of time rendering typically takes (taking the time it took for the last render call is probably a good enough guess, though you could go with a running average of the last n frames if you prefer).

One thing to be careful of doing that, though, is making sure your updates don't completely drown the renderer, so add an || to that if, compare the time since the last render call with some minimum target render frequency, a framerate low at which you're alright with introducing a little simulation lag.


float updateFrequency = 1/15;
float minRenderFrequency = 1/10;
if ( clock() - lastUpdated + averageTimePerRender < updateFrequency
|| timeSinceLastRender > minRenderFrequency
)
{
//Render in here
}

[/quote]

That looks good enough for me. In the worst case it will just render once for every game update, which would ensure a consistent game experience. Thank you!

Share this post


Link to post
Share on other sites
Additionally, there's the "fix your time step" article which you should look into.
There also was another good article linked in one of the recent (last week or two weeks ago) "game programming" or "general programming" threads, but unfortunately I don't remember the exact one.

Share this post


Link to post
Share on other sites
Quote:
Original post by jsepia
My game currently detects the number of cores. If it's running on two or more cores, then it spawns a game thread that is meant to run at a constant 15 fps, and a gfx thread which just renders anytime the game is not busy (which usually results in 120-200 fps).
I would just stick with that whether or not the CPU has several cores. Why? Because:
1. it will work just fine even on a single core (unless you do something severely wrong)
2. it will work on multi-core
3. you only have to debug one code path
4. you really don't want to have two independent code paths
5. read 3. and 4. again

Modern operating systems run several threads on one core just fine. There will be some context switching of course, but it isn't that bad really. As your game thread runs at a fixed rate of only 15 Hz (and some things can run even slower), it will likely sleep most of the time (unless you do an insane amount of calculations). Therefore, there is enough CPU time left for your render thread to run without concurrency.
On top of that, the render thread will block every now and then anyway (necessarily), during which time the game thread will use the CPU time without concurrency.


Additionally, you could squeeze out some "extra power" with additional worker threads according to the number of cores if necessary, but that will become a lot more complicated.

On the positive end, you might in theory divide your work by N cores and get a (more or less) N time increase in throughput. Amdahl tells us that things look a bit different for most real life problems, but in the end it usually doesn't matter, as "faster" is still better, even if it doesn't mean "4 times as fast".
You will not want to have too few or too many threads. Too few threads leave processors idle, too many cause context switches which are not free.
One important detail to consider is that hyperthread siblings are not fully qualified cores, they are more like "parasites" that run only if the real core isn't using a resource at the moment or if the real core is stalled in a cache miss or similar. Thus, you will not want to consider a HT sibling for a heavy worker thread.
And here the problem starts: You have CPUs with real cores and CPUs with real cores and hyperthreading siblings, and you have AMD CPUs (and possibly others) which lie about it. You need to query the number of processors, and you need to find out if they're all real cores or half of them are siblings. Now, if you care about older single-core processors, you probably care about non-Vista Windows versions too, and here's your problem, because then GetSystemInfo() and __cpuid() is pretty much all you have at hand.

Now try and work out a scheme that doesn't require Vista as a minimum, and neither leaves behind idle cores nor overloads cores, if all you have is a total number and 20-30% of the CPUs will lie about being hyperthreaded when they aren't. It's not pretty.
What I'm doing (and probably most others) is to assume num_cpu = num_cpu/2 +1; if hyperthreading is advertised. This will make lying CPUs with 3 or more cores perform worse, but hey that's their problem. It may not be the absolute optimum, but it does seem to work ok for most CPUs (including non-HT dual cores that pretend HT).

Share this post


Link to post
Share on other sites
Quote:
Original post by samoth
1. it will work just fine even on a single core (unless you do something severely wrong)
2. it will work on multi-core
3. you only have to debug one code path
4. you really don't want to have two independent code paths
5. read 3. and 4. again


Sounds wise.

Quote:
Original post by samoth
On top of that, the render thread will block every now and then anyway (necessarily), during which time the game thread will use the CPU time without concurrency.


It's this way for now, but maybe I'll just put rendering and game in the same thread (using what I learned here) while another thread takes care of processing big amounts of data like collision maps and AI (dividing threads by amount of data makes more sense than dividing them by function). It will not need to be perfectly synced to the game, as long as it's always a few frames ahead.

Quote:
Original post by samoth
...assume num_cpu = num_cpu/2 +1; if hyperthreading is advertised. This will make lying CPUs with 3 or more cores perform worse, but hey that's their problem.


Thanks for pointing out the HT issue, I did some research and found this snippet: http://software.intel.com/en-us/articles/number-of-logical-processors-per-physical-processor/
Maybe I could just do
num_cpu = num_cpu / LogicalProcessorsPerPackage()
? In theory I would be getting the same results, but my code would be a little easier to understand for me.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this