Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Building a box with texture on one side in libgdx - performance

In a 3D scrabble-like game I have a performance issue of getting only 8 FPS. Narrowed it down to the letter tiles. There are 100 of them and decreasing their number improves performance rapidly. I ruled out texture binding because speed does not improve even when all tiles has the same texture like on the screenshot.

screenshot

This is how I create each box:

public static void createModel() {
  matWhite = new Material(ColorAttribute.createDiffuse(Color.WHITE));
  ModelBuilder modelBuilder = new ModelBuilder();
  modelBuilder.begin();
  MeshPartBuilder tileBuilder;
  tileBuilder = modelBuilder.part("top", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, matWhite);
  tileBuilder.rect(-0.45f, 0.1f, 0.45f,   0.45f, 0.1f, 0.45f,    0.45f, 0.1f, -0.45f,  -0.45f, 0.1f, -0.45f,  0f, 1f, 0f);
  tileBuilder = modelBuilder.part("bottom", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
  tileBuilder.rect(-0.45f, 0f, -0.45f,    0.45f, 0f, -0.45f,     0.45f, 0f, 0.45f,     -0.45f, 0f,  0.45f,    0f, -1f, 0f);
  tileBuilder = modelBuilder.part("front", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
  tileBuilder.rect(-0.45f, 0.1f, 0.45f,  -0.45f, 0f, 0.45f,      0.45f, 0f, 0.45f,      0.45f, 0.1f,  0.45f,    0f, 0f, 1f);
  tileBuilder = modelBuilder.part("left", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
  tileBuilder.rect(-0.45f, 0.1f, 0.45f,  -0.45f, 0.1f, -0.45f,  -0.45f, 0f, -0.45f,    -0.45f, 0f,  0.45f,    -1f, 0f, 0f);
  tileBuilder = modelBuilder.part("right", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
  tileBuilder.rect( 0.45f, 0.1f, 0.45f,   0.45f, 0f, 0.45f,      0.45f, 0f, -0.45f,     0.45f, 0.1f,  -0.45f,    0f, 0f, 1f);
  tileBuilder = modelBuilder.part("back", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
  tileBuilder.rect(-0.45f, 0.1f, -0.45f,  0.45f, 0.1f, -0.45f,   0.45f, 0f, -0.45f,    -0.45f, 0f,  -0.45f,    0f, 0f, 1f);
  modelTile = modelBuilder.end();
}

public void createModelInstance(com.badlogic.gdx.assets.AssetManager assetManager) {
  Texture texTile = assetManager.get("textures/" + textureFile + ".jpg", Texture.class);
  Material mat = new Material(TextureAttribute.createDiffuse(texTile));
  modelInstance = new ModelInstance(modelTile); 
  modelInstance.nodes.get(0).parts.get(0).material.set(mat);
}

While experimenting, I found that speed goes up to 25 FPS (even with all 100 textures being used) if I replace the mesh part building code with creating a simple box (which shows the texture on all sides so it's not very useful for this game). Their createBox method also calls rect() 6 times but so far I fail to see the difference.

modelTile = modelBuilder.createBox(0.9f, 0.1f, 0.9f, matWhite, Usage.Position | Usage.Normal | Usage.TextureCoordinates);

What am I doing wrong in the first code? What would be the correct way to create a box with texture on one side only?

like image 462
Arthur Avatar asked Jan 16 '14 12:01

Arthur


1 Answers

Each time you call modelBuilder.part it implies a draw call. Since they are quite expensive, you really want to keep the number of draw calls to a minimum. Moreover, since you have different vertex attributes, it will have to switch mesh and shader in between the first and other parts. You'll also want to keep the number of mesh and shader switches to a minimum.

So, to start optimizing you'll probably want to combine the parts with the same attributes:

tileBuilder = modelBuilder.part("top", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, matWhite);
tileBuilder.rect(-0.45f, 0.1f, 0.45f,   0.45f, 0.1f, 0.45f,    0.45f, 0.1f, -0.45f,  -0.45f, 0.1f, -0.45f,  0f, 1f, 0f);
tileBuilder = modelBuilder.part("others", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal, matWhite);
tileBuilder.rect(-0.45f, 0f, -0.45f,    0.45f, 0f, -0.45f,     0.45f, 0f, 0.45f,     -0.45f, 0f,  0.45f,    0f, -1f, 0f);
tileBuilder.rect(-0.45f, 0.1f, 0.45f,  -0.45f, 0f, 0.45f,      0.45f, 0f, 0.45f,      0.45f, 0.1f,  0.45f,    0f, 0f, 1f);
tileBuilder.rect(-0.45f, 0.1f, 0.45f,  -0.45f, 0.1f, -0.45f,  -0.45f, 0f, -0.45f,    -0.45f, 0f,  0.45f,    -1f, 0f, 0f);
tileBuilder.rect( 0.45f, 0.1f, 0.45f,   0.45f, 0f, 0.45f,      0.45f, 0f, -0.45f,     0.45f, 0.1f,  -0.45f,    0f, 0f, 1f);
tileBuilder.rect(-0.45f, 0.1f, -0.45f,  0.45f, 0.1f, -0.45f,   0.45f, 0f, -0.45f,    -0.45f, 0f,  -0.45f,    0f, 0f, 1f);

And to optimize further (eliminating all mesh switches), you can use the same vertex attributes. Of course this will add texture attributes to the sides without a texture. But as long as the material doesn't have a texture, they will be ignored.

tileBuilder = modelBuilder.part("top", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, matTop);
tileBuilder.rect(-0.45f, 0.1f, 0.45f,   0.45f, 0.1f, 0.45f,    0.45f, 0.1f, -0.45f,  -0.45f, 0.1f, -0.45f,  0f, 1f, 0f);
tileBuilder = modelBuilder.part("others", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, matOthers);
tileBuilder.rect(-0.45f, 0f, -0.45f,    0.45f, 0f, -0.45f,     0.45f, 0f, 0.45f,     -0.45f, 0f,  0.45f,    0f, -1f, 0f);
...

To optimize even more (eliminating all shader switches and reducing the number of draw calls), you can add a single pixel to the texture which will be used by the sides and set the UV range accordingle:

tileBuilder = modelBuilder.part("top", GL10.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, matWhite);
tileBuilder.setUVRange(0,0,1,1);
tileBuilder.rect(-0.45f, 0.1f, 0.45f,   0.45f, 0.1f, 0.45f,    0.45f, 0.1f, -0.45f,  -0.45f, 0.1f, -0.45f,  0f, 1f, 0f);
tileBuilder.setUVRange(0,0,0,0); // Assumes the top left pixel is the correct color for the other sides
tileBuilder.rect(-0.45f, 0f, -0.45f,    0.45f, 0f, -0.45f,     0.45f, 0f, 0.45f,     -0.45f, 0f,  0.45f,    0f, -1f, 0f);
...

Now, if you still experience performance issues, you can consider combining multiple tiles into a single model (part: draw call). But that'll probably not immediately necessary with above changes.

More info: http://blog.xoppa.com/behind-the-3d-scenes-part1/

like image 159
Xoppa Avatar answered Nov 17 '22 19:11

Xoppa