Page 1 of 1

Severe FPS differences with varying processors

Posted: Wed Oct 22, 2008 8:05 am
by Aelis440
Hello everyone,

I had a question regarding FPS. I recently adjusted the code of my project so that units could turn and run "identically" on different computers by using a system which ran updates based on 'time' so that FPS did not affect movement (to avoid units moving faster on faster computers). However, in doing this, I found a peculiar difference among 3 different computers, here are their descriptions:

Computer 1: Laptop, pretty old, kinda slow (4 years). 1.73 GHz processor (intel)
Computer 2: Desktop, decent shape, 2-3 years old. 2.01GHz processor (amd)
Computer 3: Desktop, pretty much a beast, recently bought, dual core 3.01 GHz processor (amd)


Now, what is odd about this is that the delta times on Computer 1 are about 5-6 milliseconds. Meaning, it takes the computer to run an entire 'game loop' in this amount of time. On Computer 2, this time is about 2-3 seconds. Now, the SUPER odd thing is you would expect Computer 3 to run these game loops in less time, say 1 millisecond each. However, when we cout these numbers, we consistently get this sequence: "0, 0, 0, 0, x" where x is either 15, or 16 milliseconds. This would mean mean it ran 4 loops in less than a second, and the fifth loop in 15-16 milliseconds.

Note that all of these numbers increase when we slow the computers down by adding in a bunch of models into the view of the camera. However, this does not prevent Computer 3 from changing its pattern, it will just go "0, 0, 0, 31" for instance.

The reason this is such a huge issue is that the whole theory behind delta time assumes that faster computers will give u smaller delta time numbers, so you can usually depend on that during your calculations. But with these fluctuating values (a slow computers being high, a decent computer being low, and a really fast computer being REALLY high), I cannot write a formula for my movements which would be "fair" to all computers.

Finally, I think this may have to due with Computer 3 being a dual core processor. Any insight on the situation would be greatly appreciated.


Here is the code of our main:

Code: Select all

int main()
{
	// Create everything needed for the basic game engine to run.  If any of these fail, nothing will be able to continue.
	// We run a check at the end to make sure everything loaded properly.
	IrrlichtDevice* device = createDevice(EDT_OPENGL, dimension2d<s32>(1280, 1024), 16, false);
	//device->getFileSystem()->changeWorkingDirectoryTo("C:/Frontier/");  //Sets the working Directory
	IVideoDriver* driver = device->getVideoDriver();
	ISceneManager* smgr = device->getSceneManager();
	IGUIEnvironment* env = device->getGUIEnvironment();
	ISoundEngine* engine = createIrrKlangDevice();
	// The returned timer is automatically created when you create a device.
	ITimer* timer = device->getTimer();
	if (device == 0 || driver == 0 || smgr == 0 || env == 0 || engine == 0)
		return 1;	// 1 indicates that something went wrong with one of these classes.
					// Look into Exceptions so that if this occurs to a player, we can print useful information to the screen.
	

	// This tells the video driver to draw 32bit images when drawing textures.
	driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true); 
	// This tells the video driver to create mip-maps while drawing textures.
	driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, true);
	Manager manager(device, engine, smgr, driver, env);	// Create the event receiver
	device->setEventReceiver(&manager); //Set the event receiver, this MUST stay constant throughout the program.
	manager.onLoad();  // Initializes everything needed for the beginning of the program.

	// All of these variables are used to control the speed of the things in the game, NOT the FPS.
	// We have made the two indepedent of each other so the game plays almost identical on
	// faster and slower computers.
	int frameCounter = 0;
	double deltaTime;
	double oldTime = timer->getTime();
	double timeOfLastLoop = timer->getTime();
	double timeDifference;	// This is only used to make the math a little more readable where it is used.
	double currentTime;

	ITexture * mouseTexture = driver->getTexture("media/hand.tga");
	position2d<s32> mouseAdjustment;
	mouseAdjustment.X = device->getCursorControl()->getPosition().X + 1;
	mouseAdjustment.Y = device->getCursorControl()->getPosition().Y + 1;
	IGUIImage * mouse = env->addImage(mouseTexture, mouseAdjustment);
	device->getCursorControl()->setVisible(false);
	

	// This is the 'Game Loop' as long as it runs, the game will continually draw and redraw everything on the screen.
	// In almost all cases, this loop is not changed directly.  The only way the game changes is through the event receiver.
	while(device->run())
	{
		
		if (device->isWindowActive())
		{
			deltaTime = timer->getTime() - timeOfLastLoop;
			

					cout<< deltaTime << endl;


			mouseAdjustment.X = device->getCursorControl()->getPosition().X + 1;
			mouseAdjustment.Y = device->getCursorControl()->getPosition().Y + 1;
			if(mouse != 0)
			{
				mouse->remove();
				mouse = 0;
			}
			if(!manager.getRButtonIsDown())
			{
				mouse = env->addImage(mouseTexture, mouseAdjustment);
			}
		
			// Allows the game to be 'unpaused' and restarts the game timer when the window becomes active.
			// See the 'else' statement below for clarity.
			if (timer->isStopped())  
				timer->start();
		
			// Record the time between now and the previous loop


			// This allows the manager to know what that time is so it may use it in its calculations.
			// However, with really fast computers, often, deltaTime will be 0 since the 'Game Loop'
			// is running so fast.  This check prevents jagged movement when deltaTime is 0.
			if(deltaTime > 0)
			{
				manager.setDeltaTime(deltaTime);
				manager.updateKeyInput();	 //Updates the game based on any key inputs.
				manager.updateGameplay();
				manager.checkDistances();
			}

				
			// This allows the FPS to be displayed every time we have gone through 500 frames.
			if(frameCounter == 500)
			{
				currentTime = timer->getTime();					// Tag the time since this "time" will be used in two different
				timeDifference = currentTime - oldTime;			// calculations.  This allows us to not call getTime() twice,
				oldTime = currentTime;							// which, if we did, would cause us to "lose" milliseconds in
																// our calculations.
				manager.updateFPS(frameCounter / timeDifference * MILISECONDS_PER_SECOND);
				frameCounter = 0;	// reset the frame counter
			}

			driver->beginScene(true, true, 0);	
			
			// Anything you want to see BEFORE the screen updates (per loop), must be inserted	BEFORE smgr->drawAll();
			smgr->drawAll();

			manager.updateAspects(); 
			// Any GUI aspects that need to be seen BEFORE the screen updates (per loop), must be inserted BEFORE env->drawAll();
			env->drawAll();
			
			// This tells the video driver to finish drawing the scene, else we would never be able to continue.
			driver->endScene();

			frameCounter++;
         timeOfLastLoop = timer->getTime();
		}

		else
		{	// This allows us to pause the game and not have it 'accelerate' when tabbed out of, then back into by stopping the virtual timer from updating.
			if (!timer->isStopped())
				timer->stop();
		}
	}

	// This ends the program and exits.  As a convention, main is always of 'int' type and therefore must return a value.
	// We return 0 to indicate that the program ran to completion.
	// Any other error would return another Counter(i.e. return 1 that we saw above) for debugging purposes.
	device->drop();
	return 0;
}


Note that the (if deltaTime > 0) did NOT affect the values for computer 3, or any computer for that matter.

Posted: Wed Oct 22, 2008 9:30 am
by vitek
You don't say which version of Irrlicht you're using, but if I had to guess, I'd say you are using .

The issue is that older versions of the Irrlicht timer code use GetTickCount() instead of QueryPerformanceCounter() on multi-core systems. The function GetTickCount() is not nearly as accurate. If I remember correctly it has a 15ms resolution, which is not very good.

You could either update os.cpp and rebuild the library, or you could write some simple code to keep a running average of the elapsed time.

Travis

Posted: Wed Oct 22, 2008 10:13 am
by rogerborg
Urgh, yes, that fix didn't make it into 1.4.2.

You could either:

Download the SVN trunk.

Replace the source/Irrlicht/os.cpp file in your 1.4.2 SDK with this version, then recompile Irrlicht.

Get a trunk build from the nightly builds server (which seems to be down at the time of writing).

Posted: Mon Oct 27, 2008 4:15 pm
by Ethan
Flame on me if this is a dumb idea, but wouldn't it be possible to just do something like

Code: Select all

f32 deltaTime = 1.0f / myVideoDriver->getFPS();
to get a deltaTime?
At least until this bug is fixed in irrlicht timing.

Posted: Mon Oct 27, 2008 4:16 pm
by JP
No, because getFPS() is only updated once a second so isn't going to be very accurate ;)

Posted: Mon Oct 27, 2008 4:27 pm
by rogerborg
<pedant>Not more frequently than every 1500 ms</pedant>

It's not obvious from the API though, so I've updated the comment in SVN 1650 to hopefully make it clearer what getFPS() is and isn't for.

Posted: Mon Oct 27, 2008 8:33 pm
by Ethan
why does no one get a bug fixed irrlicht.dll for download, instead of letting everyone compile it themselves?

would be cool, this bug really sucks, i spend quite some hours looking
for a bug in my framework.

edit:

i recompiled the irrlicht.dll and it works great now, thanks for that fix
but i still think it's not cool to leave everyone with dual core with the broken timer alone, till hes desperate enough to watch the forum.

Posted: Mon Oct 27, 2008 10:28 pm
by hybrid
There's a nightly build server which offers the most recent SVN versions for direct use.

Posted: Mon Oct 27, 2008 10:42 pm
by Ethan
yea well okay, i missed that one. ;) my fault.
too fast reading...

well then theres still the buggy dll in the sdk which drives users (especially irrlicht newbies like me) nuts.
you have to know that theres a nightly build, first.

i hope you know what i want to say, it's not very comfortable to have to find the bug yourself, to find the right words for the forum search, and then download a new build or compile it yourself.

Posted: Tue Oct 28, 2008 12:08 am
by hybrid
This fix is already scheduled for Irrlicht 1.5, it just takes some time until all those changes will go into a stable release.

RE

Posted: Tue Oct 28, 2008 10:47 pm
by Aelis440
Thanks for all the help :). I plan on using the SVN, on a side note... rough estimate on 1.5? weeks, months?

Posted: Mon Feb 02, 2009 2:44 pm
by arnir
is now fixed? (in 1.5? )

Posted: Mon Feb 02, 2009 3:53 pm
by hybrid
At least the mentioned changes are in. Whether it fixes all those issues is not guaranteed.