Page 1 of 1

Performance when rendering to texture

Posted: Mon Nov 18, 2013 11:51 am
by SpyM
Hello,

I'm developing a 3D football game with Irrlicht 1.7.3 on Ubuntu, and I'm having performance problems when adding jersey numbers on my players textures. The application drops to 5 FPS instead of the usual 60 FPS.

My players share the same mesh, which is a B3D model. The textures I apply on them have the same size 2048*2048 pixels (with PNG type), and they are very similar. Only the colors change, according to the team for which each player competes.
My font for jersey numbers is stored using the XML format, associated with a 1350*250 pixels image representing the 10 digits from 0 to 9. The digits are quite big because they have to be visible from medium range on the back of the players, and also because the texture is also quite big.

To construct my player scene node, I do this :

Code: Select all

 
IAnimatedMesh* mesh = sceneManager->getMesh(mModelPath);
mNode = sceneManager->addAnimatedMeshSceneNode(mesh);
mTexture = driver->getTexture(mTexturePath);
mNode->setMaterialTexture(0, mTexture);
mRenderTexture = driver->addRenderTargetTexture(mTextureSize);
mNode->setMaterialTexture(0, mRenderTexture);
 
Please note I am really using setMaterialTexture two times. It seems stupid when you see it here, but it may not be. Indeed, I gathered all the instructions related to the scene node and textures in the whole class hierarchy to show it to you in just one piece of code.

When it comes to actual rendering, I process each player in the same way.
I get his render texture (RT). I draw his colored texture on this RT. Then I draw his jersey number on this RT.

Here is the code for scene rendering :

Code: Select all

 
mDriver->beginScene(
            true, // clear back-buffer
            true, // clear z-buffer
            mBgColor);
 
for(std::map<int, Player*>::iterator i = players.begin();
        i != players.end(); ++i) {
    Player* p = i->second;
 
    // Virtual surface where to draw
    ITexture* rt = p->getRenderTexture();
    // Actual player texture with its color but without jersey number
    ITexture* texture = p->getTexture();
 
    // Now we draw on texture instead of window
    mDriver->setRenderTarget(rt);
    mDriver->setMaterial(mDriver->getMaterial2D()); // fix OpenGL issue in Irrlicht 1.7.3
    // Draw actual player texture
    mDriver->draw2DImage(texture, vector2di(0, 0));
 
    mDriver->setMaterial(mDriver->getMaterial2D());
    // Draw jersey number over it
    mJerseyFont->draw(p->getJerseyText(),
                      p->getJerseyTextRect(),
                      mJerseyTextColor, true, true);
 
    // We go back to window (necessary to be able to switch, see API)
    mDriver->setRenderTarget(0, true, true, mBgColor);
}
 
mSceneManager->drawAll();
 
mDriver->setMaterial(mDriver->getMaterial2D());
mGui->drawAll();
 
mDriver->endScene();
 
Could you please help me to find what I've done wrong ?

Thanks for your help ;)

Re: Performance when rendering to texture

Posted: Mon Nov 18, 2013 12:17 pm
by mongoose7
So you're rendering to texture 22 times per frame? And each RTT is 4*2048*2048 = 16 MB? 22*16MB = 330 MB.

Why don't you just build the textures on the CPU at the beginning. In fact, why don't you have only one texture for the shirt and give the player a rectangle for the number and render the player with the two textures, one for the shirt and the other for the number. You can colour the shirt using vertex colours.

Re: Performance when rendering to texture

Posted: Mon Nov 18, 2013 12:41 pm
by SpyM
I would like to color my players only by changing the texture, since the vertex coloring would be much more complicated to handle when having complex jerseys.

So you think I have to draw the texture with the number only once at the beginning and then save it in a ITexure* member to avoid costly calls to setRT. Is this correct, or do you simply think I need to execute the for iteration only once instead of doing it at each frame ?
However, how can I draw the number on a given texture ? IGUIFont does not provide anything else than draw method, which requires RTT. I also noticed IImage might do the trick, but unfortunately we cannot use IGUIFont on it.
Am I forced to store all the images for each possible number (say from 1 to 100), or can I continue to use my font ?
I might also "hack" the problem by drawing my charset manually without IGUIFont.

What is the best solution ?

Re: Performance when rendering to texture

Posted: Tue Nov 19, 2013 1:31 am
by mongoose7
Your textures are stored in files on the disk? You can also create numbers in, say, Paint. Now load a texture and a number in memory. Write the number over the texture and then save it out (or convert it to a texture).

But, if you use shaders, you could write the number over the texture dynamically. Find where the number needs to go on the texture and create another texture with just the number on it, at the right location. Pass both textures to the shader. Wherever the number colour is white, draw the number, otherwise draw the texture. Just like chromakeying. Also, you should be able to write the colour of the texture in the shader, so you only need one texture for the colour and one texture each for each of the numbers.

Re: Performance when rendering to texture

Posted: Tue Nov 19, 2013 2:19 pm
by SpyM
I gave some memory for performance by executing the "for" iteration only once at the first rendering. I will go for shaders or "completely static" textures later if I need more memory.


Thank you for your answers, I really appreciate your help :D

Re: Performance when rendering to texture

Posted: Tue Dec 03, 2013 5:49 pm
by Adversus
Prebuild the shirts for each player as textures once when the match is loading, both colors (although I would simply use a texture per team as anything else will look crap and won't allow for detail) and numbers/names (do a setRenderTarget and call IGuiFont::draw)

btw I've used fonts when rendering to a texture. Works fine.