iTorque 2D engine efficiency boost
by Daniel Liverance · in iTorque 2D · 08/10/2011 (6:30 am) · 31 replies
Hello everyone.
-- note, this is more a story just below, if you are just here to get the performance boost,
scroll all the way to the bottom and find the code (it's simple I promise). otherwise enjoy the story :)
--------------- STORY ---------------
I just recently finished work on a game for the iPad called Space Fart (you can find a blog about it on GarageGames here -> Blinker Studios - Space Fart)
I began this project by running a simple test project on an iPad (1st Generation) and I printed out the frame rate to see how well it did.
I was mortified when I saw that with ONLY a background of 1024 x 1024 pixels it ran at (I forget now, and don't have the iPad with me to reproduce) about 30 - 35 fps. That was ONLY A BACKGROUND!!! Obviously when we added ANYTHING else to the scene the FPS dropped even further until our game was sporting a nice 15 - 20 fps. This was just simply not acceptable.
So what I did was run some profiling tools that I have on my Mac and one of them is called Open GL ES Performance Detective (pretty awesome name) and what it does is: it tells me the FPS of a running app on a device that is using OpenGL ES (which iTorque does use) and when it drops to a level that I find unacceptable I click the Collect Evidence button and it immediately starts checking the app to see what is going on and tells me the problem.
When I ran the tool on Space Fart for the iPad, this is what it told me:
As you can see, it is saying that the FPS is limited by the graphics pipeline, and it offers some suggestions such as reducing the amount of pixels pushed to the screen and so forth. BUT the most notable is the one at the bottom where it says that the app is using alpha tests. I thought that this was normal because obviously you need to take out the alpha pixels in an image that uses alpha or else you get a big white spot wherever the alpha pixels were.
well after this I did some research on OpenGL ES for IOS devices and I came across this page -> Open GL ES Tuning .
If you read through that page it offers a lot of tweaks that will improve the graphics performance of an app, but at the very bottom of the page it mentions that you need to avoid Alpha testing using GL_ALPHA_TEST. I took a look through the engine and sure enough, I found it. Now I thought that commenting out GL_ALPHA_TEST would make white pixels appear on every image that I drew that had alpha enabled, but I soon found out that Blending is the fix for that. If you turn off blending AND comment out GL_ALPHA_TEST the white pixels will appear, but if you turn on Blending and leave out Alpha test, the alpha pixels are blended with actual coloured pixels and the result is as if the alpha pixels were ignored.
This is good news because on every iTorque game, blending is on by default for every scene object. So if you have a background of any type, and blending is enabled on it, you do not need to do an alpha test.
So I went and commented out the glEnable(GL_ALPHA_TEST) and glAlphaFunc(.....) and ran my projects again to find that Space Fart, which had a measly 23 FPS, now had a decent 45 - 50 fps. As well the test project with just the background went from 30 - 35 FPS to a solid 60 FPS.
Incredible, its like a 300% speed boost with no catch. At least I believe there is no catch (I would appreciate any comments below, tell me if there is any trouble). The reason apparently is that GL_ALPHA_TEST disables hardware optimizations for the fragment shader.
So I think that EVERYONE that is using iTorque needs to read this code fix, because I also saw a performance boost on the iPhone 3G AND the iPhone 4. (I am also 100% convinced that this fix will allow for at LEAST 30 up to 40 fps using Retina Display, a good tutorial to get that going found here -> Retina Display by Pedro Vicente , seeing as the iPad 1 and iPhone 4 have the exact same CPU and the iPad has a higher resolution.)
So thank you for following along, it was a long story I know, but treat yourself to the code down below. it seems like its a small amount of code but enjoy anyways :)
--------------- Story End, Time for the Code ----------------
comment out lines 2918 and 2919 inside of "iTorque2D1_5_Preview2/engine/source/T2D/t2dSceneWindow.cc". (change iTorque2D1_5_Preview2 to whatever version you are currently using.
this is what it should look like before and after:
before:
and After
Thanks again and keep an eye out for any more apps from Blinker Studios ;)
-- note, this is more a story just below, if you are just here to get the performance boost,
scroll all the way to the bottom and find the code (it's simple I promise). otherwise enjoy the story :)
--------------- STORY ---------------
I just recently finished work on a game for the iPad called Space Fart (you can find a blog about it on GarageGames here -> Blinker Studios - Space Fart)
I began this project by running a simple test project on an iPad (1st Generation) and I printed out the frame rate to see how well it did.
I was mortified when I saw that with ONLY a background of 1024 x 1024 pixels it ran at (I forget now, and don't have the iPad with me to reproduce) about 30 - 35 fps. That was ONLY A BACKGROUND!!! Obviously when we added ANYTHING else to the scene the FPS dropped even further until our game was sporting a nice 15 - 20 fps. This was just simply not acceptable.
So what I did was run some profiling tools that I have on my Mac and one of them is called Open GL ES Performance Detective (pretty awesome name) and what it does is: it tells me the FPS of a running app on a device that is using OpenGL ES (which iTorque does use) and when it drops to a level that I find unacceptable I click the Collect Evidence button and it immediately starts checking the app to see what is going on and tells me the problem.
When I ran the tool on Space Fart for the iPad, this is what it told me:

As you can see, it is saying that the FPS is limited by the graphics pipeline, and it offers some suggestions such as reducing the amount of pixels pushed to the screen and so forth. BUT the most notable is the one at the bottom where it says that the app is using alpha tests. I thought that this was normal because obviously you need to take out the alpha pixels in an image that uses alpha or else you get a big white spot wherever the alpha pixels were.
well after this I did some research on OpenGL ES for IOS devices and I came across this page -> Open GL ES Tuning .
If you read through that page it offers a lot of tweaks that will improve the graphics performance of an app, but at the very bottom of the page it mentions that you need to avoid Alpha testing using GL_ALPHA_TEST. I took a look through the engine and sure enough, I found it. Now I thought that commenting out GL_ALPHA_TEST would make white pixels appear on every image that I drew that had alpha enabled, but I soon found out that Blending is the fix for that. If you turn off blending AND comment out GL_ALPHA_TEST the white pixels will appear, but if you turn on Blending and leave out Alpha test, the alpha pixels are blended with actual coloured pixels and the result is as if the alpha pixels were ignored.
This is good news because on every iTorque game, blending is on by default for every scene object. So if you have a background of any type, and blending is enabled on it, you do not need to do an alpha test.
So I went and commented out the glEnable(GL_ALPHA_TEST) and glAlphaFunc(.....) and ran my projects again to find that Space Fart, which had a measly 23 FPS, now had a decent 45 - 50 fps. As well the test project with just the background went from 30 - 35 FPS to a solid 60 FPS.
Incredible, its like a 300% speed boost with no catch. At least I believe there is no catch (I would appreciate any comments below, tell me if there is any trouble). The reason apparently is that GL_ALPHA_TEST disables hardware optimizations for the fragment shader.
So I think that EVERYONE that is using iTorque needs to read this code fix, because I also saw a performance boost on the iPhone 3G AND the iPhone 4. (I am also 100% convinced that this fix will allow for at LEAST 30 up to 40 fps using Retina Display, a good tutorial to get that going found here -> Retina Display by Pedro Vicente , seeing as the iPad 1 and iPhone 4 have the exact same CPU and the iPad has a higher resolution.)
So thank you for following along, it was a long story I know, but treat yourself to the code down below. it seems like its a small amount of code but enjoy anyways :)
--------------- Story End, Time for the Code ----------------
comment out lines 2918 and 2919 inside of "iTorque2D1_5_Preview2/engine/source/T2D/t2dSceneWindow.cc". (change iTorque2D1_5_Preview2 to whatever version you are currently using.
this is what it should look like before and after:
before:
// Setup new viewport.
dglSetViewport(updateRect);
// Set ModelView.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Enable Alpha Test.
glEnable ( GL_ALPHA_TEST ); //<------------------------------- you want to comment out this line
glAlphaFunc ( GL_GREATER, 0.0f );//<------------------------------- and this line
glDisable ( GL_DEPTH_TEST );
//glEnable ( GL_DEPTH_TEST );
//glDepthFunc ( GL_LEQUAL );
// implement "Don't Render Object" functionality. Hide it before we render, unhide it afterwards.
bool previousDontRenderObjectVisibility = false;
if ((mpDontRenderObject != NULL) && mpDontRenderObject->mVisible)
{
previousDontRenderObjectVisibility = true;
mpDontRenderObject->mVisible = false;
}and After
// Setup new viewport.
dglSetViewport(updateRect);
// Set ModelView.
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Enable Alpha Test.
//glEnable ( GL_ALPHA_TEST ); <-------------------------- Commented out
//glAlphaFunc ( GL_GREATER, 0.0f );<-------------------------- Commented out
glDisable ( GL_DEPTH_TEST );
//glEnable ( GL_DEPTH_TEST );
//glDepthFunc ( GL_LEQUAL );
// implement "Don't Render Object" functionality. Hide it before we render, unhide it afterwards.
bool previousDontRenderObjectVisibility = false;
if ((mpDontRenderObject != NULL) && mpDontRenderObject->mVisible)
{
previousDontRenderObjectVisibility = true;
mpDontRenderObject->mVisible = false;
}Thanks again and keep an eye out for any more apps from Blinker Studios ;)
About the author
I am 21 years old, and have just recently graduated from College for Game Programming (with honours) and I am now the Lead Programmer at Blinker Studios.
#22
@Johnny - Sure. No one has asked for this information yet, so I have been tight-lipped even though I'm really excited about this feature. Only a couple of select iT2D users have heard how the system works.
Background: The original task was to just add high resolution rendering (retina), which was a pretty simple task since three other people had posted the same approach (which I also went with). The feature then evolved into Universal App Support, at the request of other users and management. It was a pretty vague feature request, so I polled several members of the community to find out what that meant to them. I created a quick questionnaire and planned the feature based on that.
The high level overview consists of a few major parts:
1. Overhaul the templates and example projects. They are much lighter, no longer use commonConfig.xml, and have a different resolution handling function.
2. Add new functionality for iPhone 4 rendering (960x640). In addition to the changes to rendering code, a new feature was added that allows you to pick higher resolution images for a sprite. Completely optional, but easy to make use of. This is similar to Apple's @2x for retina and Cocos _hd texture loading. You will be able to pick that in the ImageMap editor or you can use a special naming scheme that will automatically load. For example:
smiley.jpg: Background image at 480x320
smiley_hd.jpg: Same background image, except 960x640. If you toggle "Use HD Image", you can pick the image from the editor (via file dialog) or just let the engine search for the same texture name with a _hd appendage.
3. Universal App Support was the trickiest part to figure out. The most obvious assumption is that your game will run on all devices without much effort. I've taken it a step further, allowing you to create a single level that will run on all devices.
While editing a t2dSceneObject in the editor, a new property will be available: Target Platform. You can set this to iPad, iPhone or Universal. You can change your device target in the editor, which will update your scene to show only the objects flagged for that device. This should allow you to edit your iPad scene and iPhone scene in a single level.
When you click play from the editor, you can pick your device (iPhone, iPhone 4 and iPad). The runtime will use the correct resolution and only load the objects flagged for the specified device.
The system is not finished yet and it still needs to go through some usability testing to make sure it is intuitive. In fact, when I tried to do the live video tutorial for Pong, I forgot I had half-finished functions in placed that borked the project =/.
08/15/2011 (8:34 am)
@Daniel - I removed your two duplicate posts. The original is still there, there is just a forum bug that prevents it from showing up. Hopefully this post gets it back.@Johnny - Sure. No one has asked for this information yet, so I have been tight-lipped even though I'm really excited about this feature. Only a couple of select iT2D users have heard how the system works.
Background: The original task was to just add high resolution rendering (retina), which was a pretty simple task since three other people had posted the same approach (which I also went with). The feature then evolved into Universal App Support, at the request of other users and management. It was a pretty vague feature request, so I polled several members of the community to find out what that meant to them. I created a quick questionnaire and planned the feature based on that.
The high level overview consists of a few major parts:
1. Overhaul the templates and example projects. They are much lighter, no longer use commonConfig.xml, and have a different resolution handling function.
2. Add new functionality for iPhone 4 rendering (960x640). In addition to the changes to rendering code, a new feature was added that allows you to pick higher resolution images for a sprite. Completely optional, but easy to make use of. This is similar to Apple's @2x for retina and Cocos _hd texture loading. You will be able to pick that in the ImageMap editor or you can use a special naming scheme that will automatically load. For example:
smiley.jpg: Background image at 480x320
smiley_hd.jpg: Same background image, except 960x640. If you toggle "Use HD Image", you can pick the image from the editor (via file dialog) or just let the engine search for the same texture name with a _hd appendage.
3. Universal App Support was the trickiest part to figure out. The most obvious assumption is that your game will run on all devices without much effort. I've taken it a step further, allowing you to create a single level that will run on all devices.
While editing a t2dSceneObject in the editor, a new property will be available: Target Platform. You can set this to iPad, iPhone or Universal. You can change your device target in the editor, which will update your scene to show only the objects flagged for that device. This should allow you to edit your iPad scene and iPhone scene in a single level.
When you click play from the editor, you can pick your device (iPhone, iPhone 4 and iPad). The runtime will use the correct resolution and only load the objects flagged for the specified device.
The system is not finished yet and it still needs to go through some usability testing to make sure it is intuitive. In fact, when I tried to do the live video tutorial for Pong, I forgot I had half-finished functions in placed that borked the project =/.
#23
I am not using the "standard" sceneWindow2D.loadLevel function supplied by the default Torque template, I use a function of my own that loads a class name instead; I tried your code but for some reason this name was not passed properly in the parameter list to the second "_changeLevel";
the only way I got it to work was like this; a little bit of repeated code, but it works ! :-)
08/15/2011 (8:40 am)
@DanielI am not using the "standard" sceneWindow2D.loadLevel function supplied by the default Torque template, I use a function of my own that loads a class name instead; I tried your code but for some reason this name was not passed properly in the parameter list to the second "_changeLevel";
the only way I got it to work was like this; a little bit of repeated code, but it works ! :-)
//---------------------------------------------------------------------------------------------
// button classes
//---------------------------------------------------------------------------------------------
// .......................................................................................
function ButtonHome::onMouseUp( %this, %modifier, %worldPosition, %clicks )
{
%this.schedule(0, "changeLevel" );
alxPlay("beepSound");
}
function ButtonHome::changeLevel(%this)
{
loadSceneGraph( MainMenuSceneGraph );
}
// .......................................................................................
function Level_01_Button::onMouseUp( %this, %modifier, %worldPosition, %clicks )
{
%this.schedule(0, "changeLevel" );
alxPlay("beepSound");
}
function Level_01_Button::changeLevel(%this)
{
loadSceneGraph( SceneGraphLevel_01 );
}
// .......................................................................................
function Level_02_Button::onMouseUp( %this, %modifier, %worldPosition, %clicks )
{
%this.schedule(0, "changeLevel" );
alxPlay("beepSound");
}
function Level_02_Button::changeLevel(%this)
{
loadSceneGraph( SceneGraphLevel_02 );
}
#24
@Pedro - I wasn't aware that your way of changing levels suffered the same problem that mine did. (changing the scene graph in a callback method). I'm really happy that you got it working :).
08/16/2011 (7:31 am)
@Michael - Thanks for the post fix :)@Pedro - I wasn't aware that your way of changing levels suffered the same problem that mine did. (changing the scene graph in a callback method). I'm really happy that you got it working :).
#25
Update: Just did this for Cavorite, which is built on iTGB 1.3.1, and no visible improvement on non-retina graphics.
08/17/2011 (6:28 pm)
Awesome. Been hard at work porting Cavorite to Mac and have had a few folks clamor for iPad/retina versions. Also keen on updating the TGB Kart Kit a bit to utilize 1.5's new capabilities. Update: Just did this for Cavorite, which is built on iTGB 1.3.1, and no visible improvement on non-retina graphics.
#26
ohMyGod!!!
How will toggling devices effect or play out on universal sprites? For instance, if I have a sprite (in visibility, I know I am going against POT (all) odds) but, if my sprite is one solid colour. It is 320x480. It sits in full view, taking up the entire screen in iPhone. When I switch to iPad, does it remain a 320x480 shape, and does not grow in any form to compensate the windows growth?
I am just wondering about using identical sprites in universal projects, with this feature.
////
08/17/2011 (8:21 pm)
FIRST OFF.ohMyGod!!!
Quote:
While editing a t2dSceneObject in the editor, a new property will be available: Target Platform. You can set this to iPad, iPhone or Universal. You can change your device target in the editor, which will update your scene to show only the objects flagged for that device. This should allow you to edit your iPad scene and iPhone scene in a single level.
How will toggling devices effect or play out on universal sprites? For instance, if I have a sprite (in visibility, I know I am going against POT (all) odds) but, if my sprite is one solid colour. It is 320x480. It sits in full view, taking up the entire screen in iPhone. When I switch to iPad, does it remain a 320x480 shape, and does not grow in any form to compensate the windows growth?
I am just wondering about using identical sprites in universal projects, with this feature.
////
#27
- 1024x768
- 960x640
- 480x320
Toggling hd would not cover it off due to aspect ratio etc
Sprites are straightforward but backgrounds are where we need to be able to load the correct image for the target resolution.
08/18/2011 (5:31 am)
Michael, I'm sure you've got it covered but we need support for three image varients- 1024x768
- 960x640
- 480x320
Toggling hd would not cover it off due to aspect ratio etc
Sprites are straightforward but backgrounds are where we need to be able to load the correct image for the target resolution.
#28
I thought about adding that feature in, but it was quickly deemed impractical. Objects started getting out of sink and game play revolving around resolution broke instantly. Part of the reason is due to what Scott just brought up regarding the aspect ratio. The release date for 1.5 would have been pushed back and the feature was not guaranteed to work.
@Scott - I think that can be accommodated. Right now I'm only allowing two images, but it is trivial to add a 3rd option. That's something else worth mentioning. I have been very careful to make it easy to extend this system. All "if" statements related to resolutions and device-specific info have been replaced with switch statements. There are currently only three, but it is very simple code anyone can look at and easily add new resolutions/device types.
This is planning ahead for iPhone 5, iPad HD, iWhateverDevice (102301x59 resolution). I had to work around or completely replace assumptions built into the engine a few years ago. The real benefit of this is that anyone can immediately support a new device without waiting on us, but also it would not take us long to add support for a new device.
Anyway, 3rd image selection can work like this:
smiley.jpg
smiley_ip4.jpg
smiley_ipad.jpg
Later on we can keep going:
smiley_ip5.jpg
smiley_ipad3.jpg
How does that sound Scott?
08/18/2011 (6:39 am)
@rennie - Quote:It sits in full view, taking up the entire screen in iPhone. When I switch to iPad, does it remain a 320x480 shape, and does not grow in any form to compensate the windows growth?
I thought about adding that feature in, but it was quickly deemed impractical. Objects started getting out of sink and game play revolving around resolution broke instantly. Part of the reason is due to what Scott just brought up regarding the aspect ratio. The release date for 1.5 would have been pushed back and the feature was not guaranteed to work.
@Scott - I think that can be accommodated. Right now I'm only allowing two images, but it is trivial to add a 3rd option. That's something else worth mentioning. I have been very careful to make it easy to extend this system. All "if" statements related to resolutions and device-specific info have been replaced with switch statements. There are currently only three, but it is very simple code anyone can look at and easily add new resolutions/device types.
This is planning ahead for iPhone 5, iPad HD, iWhateverDevice (102301x59 resolution). I had to work around or completely replace assumptions built into the engine a few years ago. The real benefit of this is that anyone can immediately support a new device without waiting on us, but also it would not take us long to add support for a new device.
Anyway, 3rd image selection can work like this:
smiley.jpg
smiley_ip4.jpg
smiley_ipad.jpg
Later on we can keep going:
smiley_ip5.jpg
smiley_ipad3.jpg
How does that sound Scott?
#29
So I can expect that a 16x16 object, in iPhone setting, will be that size in iPad setting.
?
Regardless, this is exciting news.
::))(((
08/18/2011 (7:12 am)
@Michael,So I can expect that a 16x16 object, in iPhone setting, will be that size in iPad setting.
?
Regardless, this is exciting news.
::))(((
#31
@Scott - Great. How about this approach? There will still be a single flag, UseHDImage. If this flag is toggled, then it will load the appropriate image depending on the device. You can still specify the image or use the appended _device to automatically load it.
08/18/2011 (8:34 am)
@rennie - That is correct.@Scott - Great. How about this approach? There will still be a single flag, UseHDImage. If this flag is toggled, then it will load the appropriate image depending on the device. You can still specify the image or use the appended _device to automatically load it.
#32
e.g. device is ipad:
- load iPad
if not found
- load i4
if not found
- load "standard" image
08/18/2011 (9:05 am)
Michael, that would work providing there is a fallback safety nete.g. device is ipad:
- load iPad
if not found
- load i4
if not found
- load "standard" image
#33
There's more code, but that basically takes care of your fallback.
08/18/2011 (9:23 am)
Pretty much how it is setup now. Here is the pseudo-codeif(mUseHDImage && deviceType == IPAD)
{
// Code for detecting hd image removed
if(HDImageExists)
szFullPathBuffer = HDImagePath;
else
szFullPathBffer = mSrcBitmapName;
}There's more code, but that basically takes care of your fallback.
#34
08/18/2011 (12:13 pm)
@Michael - Thanks for the thorough explanation.
#35
Here's what I use with my own Retina implementation
Torque Minimal Template -- Part 4. Retina Display
An example: I have 2 images, 128 and 64 pixels versions of the same
So, I was just wondering if this approach will work with the new 1.7.5 system above.
What I am doing here is
1) ipad: load the 128 image
2) iphone4: load the 128 image but display it in 64 units (this is setSize of t2dStaticSprite)
3) iphone: load the 64 image
That system looks excellent, but somehow I would like not to be dependent on any automatic image loading and be able to turn it off and do any manual loading like above.
08/24/2011 (7:49 pm)
@MichaelQuote:Add new functionality for iPhone 4 rendering (960x640).
Here's what I use with my own Retina implementation
Torque Minimal Template -- Part 4. Retina Display
An example: I have 2 images, 128 and 64 pixels versions of the same
if ( $platform $= "ipad" || ( $platform $= "windows" && $platform_simul $= "ipad") ) %obj = CreateButton( %this, "Button", "StrawberryImageMap", %this.x_score, %this.y_score, 128, 103); else if ( $platform $= "iphone4" ) %obj = CreateButton( %this, "Button", "StrawberryImageMap", %this.x_score, %this.y_score, 64, 51); else %obj = CreateButton( %this, "Button", "StrawberryImageMap_64", %this.x_score, %this.y_score, 64, 51);
So, I was just wondering if this approach will work with the new 1.7.5 system above.
What I am doing here is
1) ipad: load the 128 image
2) iphone4: load the 128 image but display it in 64 units (this is setSize of t2dStaticSprite)
3) iphone: load the 64 image
That system looks excellent, but somehow I would like not to be dependent on any automatic image loading and be able to turn it off and do any manual loading like above.
Daniel Liverance
Blinker Studios
I used the TGB to do the design, making buttons and such.
The only thing is that I ALWAYS use onMouseUp instead of onMouseDown. On the device I noticed that onMouseDown gets called a lot if you hold the touch.
Then inside the onMouseUp function I do this:
function myButton::onMouseUp(...) { changeLevel("TitleScreen"); } function changeLevel(%stringScreenName) { schedule(0, 0, "_changeLevel", %stringScreenName); } function _changeLevel(%stringScreenName) { //sceneWindow2D.endLevel(); // this gets called at the start of loadLevel anyways. sceneWindow2D.loadLevel("game/data/levels/" @ %stringScreenName @ ".t2d"); // start new "level" }the string I pass for the Screen Name is simply the name that I saved the TGB level as.
Also the reason its inside a schedule is because if you call sceneWindow2D.loadLevel inside a callback function... bad stuff happens :) So i schedule it and it works great.