Game Development Community

ITGB game loop

by T · in iTorque 2D · 03/16/2009 (1:32 pm) · 19 replies

Hi,

I'm trying to create a game loop and I'm running into performance troubles.

In my scenegraph I use the onUpdateScene call back and getSceneTime to record how long my frame took. Then I call an update on my game objects that are using time base logic.

// The scene's update this is where we'll get our elapsed time
function SceneGraph::onUpdateScene(%this)
{
// First get the current time we can restrict how often we update
%this.elapsedTime = %this.getSceneTime() - %this.startTime;

// Only update if we are above our lock time
if (%this.elapsedTime > $lockTime)
{

// Go through our list of stuff we want to update
for (%i = 0 ; %i < %this.updateObjList.getCount() ; %i++)
{
%this.updateObjList.getObject(%i).update(%this.elapsedTime);
}

// Update the scene time
%this.startTime = %this.getSceneTime();

}

}

Then when we want an object to use time based logic I just add it to the updateObjList and write an update function for it. The problem is I'm running into performance issues and I don't know if this game loop approach is just not a good idea.

Basically I have 35 objects in game and in each of the object's update function I have 6 addition , 3 multiplications, 3 divisions and 3 assignment operation. These are written in torque script. This will send my frame rate to less than 5 FPS. Now if I remove these math operation I can get about 30 FPS. Does anyone know if math operations are just that slow in torque script or does the game loop approach have a big performance hit with the engine?

Any thought or experience would be much appreciated

#1
03/16/2009 (1:45 pm)
It's mostly the game loop. Doing a lot of script per frame will slow things down.

For you though, the fix is likely very easy. Write your update function C++, and make it into a ConsoleFunction, then call that from onUpdateScene(), i.e. instead of %object.onUpdate( %time ), do myCppUpdateFunction( %object ), or somthing similar.

If you want to speed it up even more, you can call it from C++, instead of from SceneGraph::onUpdateScene()(you'll have to modify the source a bit more, but it shouldn't be very complicated).

In general, it's best to use script for callbacks and logic. For example, an end of the level GUI wouldn't be impeded at all by a large amount of scripts calls, who's going to notice an extra 30ms when the "Game Over" screen shows up? But during gameplay, try to keep script usage down, especially one that do a lot of calculations, just make it a consoleFunction and that'll give you a good increase.
#2
03/16/2009 (1:56 pm)
thanks, I will give that a try.
#3
03/16/2009 (2:30 pm)
@T: I'm in a similar boat. The game loop started out at 4.5 FPS. I optimized it up to about 8 FPS. Tonight I'm going to port the full loop over to C++. I'll let you know my results.
#4
03/16/2009 (2:45 pm)
I must say, I do reccomend against using a game loop, it is preferable to treat things in a more event oriented approach that a loop-oriented one, if your design can accomadate that.
#5
03/16/2009 (3:11 pm)
@Mat: I've certainly thought about that in my case. In fact, I might go that route. :)

UPDATE: Well, couldn't get rid of one loop. But was able to up from 8 FPS with a script loop to 18 FPS with a C++ loop. Can't argue with those results!
#6
03/17/2009 (3:24 pm)
Hi
I've convert the simple math instructions to C++ but I'm getting some odd results.

In main.cc I have
ConsoleFunction(lotsOfMath, void, 0,0, "blah")
{
float blah = 46*939/821831.23949 + 0.43842;
float blah2 = 46*939/821831.23949 + 0.43842;
float blah3 = 46*939/821831.23949 + 0.43842;
blah = 46*923/82831.239 + 150.43842;
blah2 = 46*9239/821831.23949 + 23440.842;
blah3 = 46*99/821.2392249 + 0.43842;
blah = 4*99/8211.23949 + 23.43842;
blah2 = 426*9329/8221831.23949 + 0.43842;
blah3 = 436*9349/82131.23949 + 0.43842;

Con::printf("done MAth");
}

Then I call this test math function in my object's update. Now if I just have the math operation in torque script I get about 15 FPS but when I moved it to C++ I was getting 4. This was very confusing so I'm wondering if using ConsoleFunction was the wrong way to make a "script accessible" C++ function. Also if calling a C++ through script have some overhead and when it's in a gameloop/update situation that overhead just kills the FPS.

Thanks
#7
03/17/2009 (3:29 pm)
Did you have the printf when you used a script function? Prints / echos are slow.
#8
03/17/2009 (3:50 pm)
@Chris,
yup, I also ran it without the con::printf and got the same result. I think the problem is that my game loop is still in script using OnUpdateScene, how did you go about converting the game loop to C++. Did you move the loop into main.cc?
#9
03/17/2009 (3:59 pm)
You know, I still have a "game" class that uses an onTimer callback. Within that onTimer function there's a console method called. That method has a short do-while loop inside. Moving that loop out of script and into the C++ literally gave me 8 FPS alone.

So it might be the onUpdate. I've had performance problems with that in the past... but that doesn't explain why it wouldn't be faster in C++. :-/
#10
03/18/2009 (1:48 pm)
An update on our internal tests
1) Changing incremental FOR loop to a decrement WHILE loop definitely improved performance, we saw a 20ms decrease in process time on the iPhone. This was timing a routine that first used for loop then use while loop.

2) Calling a C++ console function has an overhead that cost about 13ms on the iPhone. The reason why the math operation was slower in C++ is because the overhead was greater than the gain we got from performing the operation in C++. When we put the operation in a loop and ran the loop multiple times we saw the gain in C++.

3) The overhead for calling C++ function is a per call cost. We made a blank C++ function and called it from script 10 times in a row and the cost was about 130ms
#11
03/18/2009 (3:33 pm)
@T: Those are interesting results and similar to what I've encountered. When I merged all my console calls into a single console function, I saw a bit of improvement as well.
#12
03/18/2009 (6:05 pm)
With PUAP_SCRIPT_CHANGE, one of the major changes was reducing the number of times a function need be looked up to once per calling function. After it's found the first time , a pointer to that function is saved and used from then one.

For example, say you have some function:
function func1() {
 myconsolefunction();
}

the first time you call func1, myconsolefunction() will take the full time to run, every time thereafter it won't have to look myconsolefunction up and should be faster.

Only thing is, if you call myconsolefunction using an eval() or cal() statement, you have to look it up the slow way again.
#13
03/20/2009 (7:53 am)
Another update
Converted the math function so it was a member of a class and the same math routine that was a console function was slower than wrapping the routine as a console method.

Also just so my previous number made more sense there are about 35 objects running these math functions per frame and we are getting the per frame millisecond delta to measure performance gain.

So from what I can gather so far Console Method runs faster than Console Function. In the scenario described above we saw a gain of 5ms for our frame when we switched from Console Function to making the function a console Method. This could be due to what Mat was talking about with the function pointer being stored.
#14
03/27/2009 (12:07 pm)
In case anyone else is interested
We moved the update call inside the scengraph's OnSceneUpdate to C++ and moved our objects' update to C++ and that gave us a huge performance boost.
In isolated test our ms for the loop went from 80+ms down to .6 ms per our frame's update cycle.

Here are some of the info from our experience
Gameloop architecture is going to have a heavy overhead, if possible try to make your game as event driven as possible. The per frame operation cost in script is enormous.

script operations are slow: Even assignment operations, object access like accessing a script global are slow just about everthing done in script is slow on the iPhone.

Console Function is slower than making a console method. I think this is probably due to the function pointer being stored which is explained in comment #12

Accessing Script Variables from C++ or vice versa is slow. If you use getVariable in C++ to change a script variables it's slow. If you setup a persistent field in C++ and access that field in script it's also costly

So the solution is to pretty much convert everything to C++ try to make your game as event driven as possible. We are still in the conversion process but we definitely see tremendous amount of performance gains by doing things in C++. Also try not to cross your C++ and Script operation in a per frame basis. This won't be a problem if your game is event driven but if you are doing something like a gameloop you want to minimize C++ to Script access and vise versa.
#15
03/27/2009 (12:56 pm)
Thanks T this is great information. My basic experience so far with iTGB has agreed with this.
#16
03/28/2009 (5:42 am)
Yep pretty much the same as it was 8 months ago.

Time to change the description on the iTGB engine on the product page; "Powerful scripting language with thorough optimizations for rapid script execution." is a false advertisement.
#17
06/08/2009 (7:22 am)
An update on performance:
We've gotten our average frame rate from less than 10 frame to about mid 20s, doing pretty much everything mentioned in my previous post. One thing to keep in mind is slow down during your game loop due to newing objects. Creating new derived t2dsceneobjects takes some time to do so I recommend making a object factory that recycles object, in C++ of course.
#18
07/09/2009 (7:52 pm)
our game is out, it's attack of the dust bunnies. check it out at attackofthedustbunnies.com
#19
07/13/2009 (1:32 pm)
Glad I could help.

I actually ended up doing object recycling as well in my current project. Funny.