Performance question

So, I noticed that my performance stats looked a little strange with tonegodgui elements attached - the “switches” stats are quite inflated. In my little testing room with an incomplete GUI, I’m looking at 16/16/23 and 9/9/27 for textures and shaders respectively. Not attaching the screen drops the switches down in line with the other stats.

I put together a test case with some panels and scroll adapters and found the same thing. Interestingly, panels didn’t cause any switching. I know several of the controls in my project contributed to the total, though, including buttons, sprites, and indicators.

Some numbers:

Blank screen:
2/2/2 (T), 3/3/3 (S), 2400fps

20 scroll adapters, no text:
4/4/42 (T), 5/5/63 (S), 1530fps

200 scroll adapters, no text:
4/4/402 (textures), 5/5/603 (shaders), 165fps

200 panels:
3/3/3 (T), 4/4/4 (S), 510fps

This isn’t causing me any problems right now, obviously, but if this is something I can fix, I definitely want to do so - I want to support low-end machines as much as possible. Am I missing a step somewhere? Is this just not something I should worry about?

Here’s the test case:

[java]
package tonegodtest4;

import com.jme3.app.SimpleApplication;
import com.jme3.math.Vector2f;
import com.jme3.renderer.RenderManager;
import com.jme3.system.AppSettings;
import tonegod.gui.controls.scrolling.ScrollAreaAdapter;
import tonegod.gui.core.Screen;

public class Tonegodtest4 extends SimpleApplication {

Screen screen;

public Tonegodtest4() {
    AppSettings newSetting = new AppSettings(true);
    newSetting.setFrameRate(-1);
    setSettings(newSetting);

}
public static void main(String[] args) {
    Tonegodtest4 app = new Tonegodtest4();
    app.start();
}

@Override
public void simpleInitApp() {
    flyCam.setEnabled(false);
    screen = new Screen(this);
    guiNode.addControl(screen);

    for (int i = 0; i < 200; i++) {
        //Panel panel = new Panel(screen, "panel"+i,new Vector2f(45, 45), new Vector2f(200, 200));
        ScrollAreaAdapter panel = new ScrollAreaAdapter(screen, "scr"+i, new Vector2f(45, 45), new Vector2f(200, 200));
        screen.addElement(panel);
    }

}

@Override
public void simpleUpdate(float tpf) {
    //TODO: add update code
}

@Override
public void simpleRender(RenderManager rm) {
    //TODO: add render code
}

}
[/java]

Try enabling texture atlasing. I believe this was a major contributing factor to why it was added. If you need some help implementing secondary texture atlases, I’ll be able to assist… some of this still (especially newer feature) not documented fully.

Nope… this is not what I thought it was. I actually am not sure how to answer this. Or even why the switches are happening considering the atlas is a single texture defined at the screen level and shared for ALL elements. Maybe @nehon can help answer why this would look this way?

I was curious why Panel didn’t exhibit this behavior, so I ran a few more tests:

200 windows (with or without custom image):
4/4/402 (T), 4/4/4 (S), 225fps

200 ButtonAdapters (with or without custom image):
4/4/3 (T), 4/4/4 (S), 500fps

200 indicators (with or without custom image):
4/4/402 (T), 5/5/603 (S), 125fps

For the window, I noticed that removing the dragBar eliminated the switching. Setting it up with a null image for the drag bar removed the texture switching but blew up the shader switching to 403.

I understand texture atlasing on a basic level, but I haven’t actually worked with it yet, and I wasn’t actually aware that it was part of your GUI API. Just to see what happened, I tried turning it on with just some random image like so:

[java]
screen.setUseTextureAtlas(true, “Textures/attack.png”);
[/java]

And tried it with a couple of elements like so:
[java]
ScrollAreaAdapter panel = new ScrollAreaAdapter(screen, “scr”+i, new Vector2f(45, 45), new Vector2f(200, 200), Vector4f.ZERO, “x=0|y=0|w=80|h=80”);
Window panel = new Window(screen, “win”+i, new Vector2f(45, 45), new Vector2f(200,200), Vector4f.ZERO, “x=0|y=0|w=80|h=80”);
[/java]

On the windows, this actually got rid of the switching (though fps didn’t improve), but the scroll area adapter didn’t improve at all. In fact, my system for some reason choked trying to draw 200 scroll area adapters, so I could only test it with 20, which still took an age to load and ran at half the fps as it did without the atlas. I must be doing it seriously wrong, so any guidance you can give would be welcome. Sorry.

Does any of this information help at all?

You just enable texture atlasing from the screen:

[java]
screen = new Screen(this, “tonegod/gui/style/atlasdef/style_map.xml”);
screen.setUseTextureAtlas(true,“tonegod/gui/style/atlasdef/atlas.png”);
[/java]

This seems to have no impact on this particular issue. Which is strange at best. Once this is enabled, every Element uses a share texture + texCoords… even effects apply offsets to a single textCoord. So, in an ideal situation… the:

[java]
glActiveTexture(texIndex)
[/java]

should NEVER change, as the loaded texture at, say… index 0, should be used from the first render call to the last. Equaling exactly 0 texture swaps.

This is why I need someone more familiar with JME to answer why this might be the case.

Shader switch occurs when the gpu has to load a different shader for rendering an object, than the shader use for the previous object.
As you discovered this operation has an impact on performance.
5 shaders and ~600 shader switches looks pretty excessive.

To avoid this, the opaque bucket sorts geometry according to their material. @t0neg0d do you have a custom geometryComparator for your GUI?

Also, do you use BitmapText?

@nehon said: Shader switch occurs when the gpu has to load a different shader for rendering an object, than the shader use for the previous object. As you discovered this operation has an impact on performance. 5 shaders and ~600 shader switches looks pretty excessive.

To avoid this, the opaque bucket sorts geometry according to their material. @t0neg0d do you have a custom geometryComparator for your GUI?

No, it relies on whatever the standard comparator is for the GUI node.

GUI node sorts using Z.

@pspeed said: Also, do you use BitmapText?

Yes, but if text is never set, then the text element is never created or added to the scene. In the test case, it is not used.

I suppose I could compact the GUI atlas and the standard font into a single texture, to ensure this doesn’t happen, but I’m 100% positive that (at least in the test case above) that this is not the reason. However, this could become an issue in a text-heavy GUI.

@pspeed @nehon

EDIT: Actually, I take this back. The ScrollAreaAdapter might actually create the scrollable text element. It’s a little late for me to check atm, but I’ll definitely look into this as the culprit in the AM. If this ends up being the case, I’ll look into merging the standard font and gui atlas into a single image. This would remedy this.

Though, I’m a little confused as the GUI elements and BitmapText use the same shader (I unload JME’s BitmapFont loader and replace it with a custom font loader when the screen is created to change the material used for rendering Bitmap Text).

I’m also a little confused why both the shader and texture stats show a swap, two shaders (which does not happen here) using the same texture both would reference the same cached texture… e.i. Texture0 is still texture0 no matter what shader references it. So shouldn’t the stat display show a shader swap with no texture swap at this point?

Each text element would have to be in a different Z for it to really matter, performance-wise. Else, the text would be kind of grouped anyway.

@nehon
Sorry for the multiple tags… but the more I consider what you said in your reply, the more confused I’m getting. What would the stat display show if I was to add 6 different BitmapText to a scene (just these… same font, same shader, 6 meshes)? Would this show multiple texture and shader swaps? Or?

Seeing something like this, makes me wonder…

200 windows (with or without custom image):
4/4/402 (T), 4/4/4 (S), 225fps

This shows 4 textures being swapped 402 times… fair enough without atlasing. But if you enable texture atlasing (a single image loaded once into memory and then referenced by all elements, the swapping stat remains the same. What is it swapping if there is only one texture loaded in memory?

EDIT: OMG… Ignore me. This doesn’t happen unless what @pspeed said is the case. It was bothering me bad enough to check. It is specific to the one control. I just ran the test case using a window instead no texture/shader swapping with 200 windows. Thanks a ton, all 3 of you for pointing this out and helping me understand why it would happen. This will get fixed tomorrow.

Hard to tell without debugging really.

what if you try to use the opaque Comparator for the gui bucket?
[java]
viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Gui, new OpaqueComparator());
[/java]

Depends on what those four textures are, I guess. If there were only one texture then there would be no swapping… so something is swapping between those four somehow. Whatever those four are.

Do we have any further information on this?