Strange performance problem

I am sorry for the vague title, but i could not think of anything better.



My Problem is as follows:



I am rendering a bunch of voxels. Everything runs with acceptable fps. But, if the front, bottom left corner of the entire landscape comes into view or even the viewing frustum (covered my some other geometry), the “uniforms” count increases dramastically and the fps go down quiet a bit.



Without problem:

http://i.imgur.com/r9I5C.png



With problem:

http://i.imgur.com/D4302.png



Without problem:

http://i.imgur.com/e0CxN.png



With problem:

http://i.imgur.com/BIRYN.png



It would be awesome If any of you wise people could provide me with a hint.



Thanks,

Stefan

I guess you create some geometry there at 0/0/0 w/o realizing.

@normen said:
I guess you create some geometry there at 0/0/0 w/o realizing.

I thought that, but look at the figures. Triangles and vertices both actually go down but FPS drops by over 100....
@zarch said:
I thought that, but look at the figures. Triangles and vertices both actually go down but FPS drops by over 100....


I think you are mistaken.

At 1341 FPS, he has 56 objects and 109k triangles
At 263 FPS, he has 161 objects and 380k triangles

The bad view has almost 3 times ad many objects and almost 4 times as many triangles. Something is going on at 0,0,0 and my guess is that there is a ton of bad geometry there.

If it were me, I'd probably dump the scene graph to logs and see what's what.

@pspeed I was looking at the screenshots as pairs (since they seem to be similar shots so be more comparable):



In the top two:

390 vs 263 fps,

485k vs 382k triangles.

202 vs 161 objects



Triangle and object count has dropped in that case but performance has degraded.



In the bottom two:

1341 vs 357fps

Objects 56 vs 55

Triangles both 109k



Again object and triangles reduced, performance degraded (massively in this case).

Mmmm… yeah.



The fact that the uniforms shoot up says something about what’s in the scene there, though. When the object counts go down then the uniform count shoots up as the OP mentions.



That’s not normal and I suspect if the OP reduced it to a simple test case that the problem wouldn’t show… and if it did then that would be something to look into for sure.

Thank you for your replies so far.



I will try to reduce my code to a test case tonight and post it.

Ok, i have come closer to the problem. I’ll try to explain a bit more what is going on.



The terrain consists of voxels. For faster modification, it is divided into smaller chunks of a given size. Every chunk is added to the map node as a child (the map node itself does not contain the geometry). Since a lot of the terrain is air, half or more of the chunks are going to be empty. The high uniform count is usually equal to the amount of uniforms i get when i fill the entire terrain with blocks.



Long story short: I don’t add empty chunks anymore and the problem is gone.



Sorry for your troubles.



If it is still of interest to you, here is a (not entirely simple, sorry about that) class which should replicate the effect. The red box is the danger point.



[java]package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.light.DirectionalLight;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector2f;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.scene.Geometry;

import com.jme3.scene.Mesh;

import com.jme3.scene.Node;

import com.jme3.scene.VertexBuffer;

import com.jme3.scene.shape.Box;

import com.jme3.util.BufferUtils;

import java.util.LinkedList;

import java.util.List;

import java.util.Random;



public class Main extends SimpleApplication {



public static final int MAP_SIZE_X = 128;

public static final int MAP_SIZE_Y = 128;

public static final int MAP_SIZE_Z = 128;



public static final int CHUNK_SIZE_X = 16;

public static final int CHUNK_SIZE_Y = 16;

public static final int CHUNK_SIZE_Z = 16;



public static final int NUM_CHUNKS_X = MAP_SIZE_X / CHUNK_SIZE_X;

public static final int NUM_CHUNKS_Y = MAP_SIZE_Y / CHUNK_SIZE_Y;

public static final int NUM_CHUNKS_Z = MAP_SIZE_Z / CHUNK_SIZE_Z;



private short[][][] mapData;



Node myNode;



public static void main(String[] args) {

Main app = new Main();

app.start();

}



@Override

public void simpleInitApp() {

initData();



myNode = new Node();

createChunks();

rootNode.attachChild(myNode);



flyCam.setMoveSpeed(30.0f);



createLight();

markTheSpot();

}



public void createLight() {

DirectionalLight sun = new DirectionalLight();

sun.setDirection(new Vector3f(1,-0.5f,-2).normalizeLocal());

sun.setColor(ColorRGBA.White);

rootNode.addLight(sun);

}



/**

  • Initializes the data that is the basis for the terrain. It is important to note
  • here that "if (j < 8)" is an important part of the problem. Since a large part of the
  • data is going to be 0, most of the terrain chunks are going to be empty.

    */

    public void initData() {

    mapData = new short[MAP_SIZE_X][MAP_SIZE_Y][MAP_SIZE_Z];

    Random r = new Random();

    for (int i = 0; i < mapData.length; i++) {

    for (int j = 0; j < mapData[0].length; j++) {

    for (int k = 0; k < mapData[0][0].length; k++) {

    if (j < 8) {

    mapData[j][k] = (short)r.nextInt(2);

    } else {

    mapData[j][k] = 0;

    }

    }

    }

    }

    }



    /**
  • Create the chunks for the landscape

    /

    public void createChunks() {

    //Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    //mat.setColor("Color", ColorRGBA.Blue);

    //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");

    Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");

    for (int i = 0; i < NUM_CHUNKS_X; i++) {

    for (int j = 0; j < NUM_CHUNKS_X; j++) {

    for (int k = 0; k < NUM_CHUNKS_X; k++) {

    int[] offset = {i
    CHUNK_SIZE_X, jCHUNK_SIZE_Y, kCHUNK_SIZE_Z};

    Mesh mesh = createMesh(offset);

    Geometry geo = new Geometry("Chunk (" + i + "," + j + "," + k + ")" , mesh);

    geo.setMaterial(mat);

    myNode.attachChild(geo);

    }

    }

    }

    }



    /**
  • Creates the mesh for a specific chunk of the landscape.

    *
  • @param offset
  • @return

    */

    public Mesh createMesh(int[] offset) {

    List<Vector3f> vertice = new LinkedList<Vector3f>();

    List<Vector2f> texcoord = new LinkedList<Vector2f>();

    List<Vector3f> normals = new LinkedList<Vector3f>();

    List<Integer> indices = new LinkedList<Integer>();



    for (int i = offset[0]; i < offset[0] + CHUNK_SIZE_X; i++) {

    for (int j = offset[1]; j < offset[1] + CHUNK_SIZE_Y; j++) {

    for (int k = offset[2]; k < offset[2] + CHUNK_SIZE_Z; k++) {

    createCube(i, j, k, mapData, vertice, texcoord, indices, normals);

    }

    }

    }



    Vector3f[] verticeArray = new Vector3f[vertice.size()];

    Vector2f[] texcoordArray = new Vector2f[texcoord.size()];

    Vector3f[] normalArray = new Vector3f[normals.size()];

    int[] indexArray = new int[indices.size()];



    vertice.toArray(verticeArray);

    texcoord.toArray(texcoordArray);

    normals.toArray(normalArray);

    int counter = 0;

    for (Integer index : indices) {

    indexArray[counter] = index;

    counter++;

    }



    Mesh mesh = new Mesh();



    mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(verticeArray));

    mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(texcoordArray));

    mesh.setBuffer(VertexBuffer.Type.Normal, 3, BufferUtils.createFloatBuffer(normalArray));

    mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(indexArray));

    mesh.updateBound();



    return mesh;

    }



    /**
  • Creates the vertices, etc. for one voxel directly into the provided lists.

    */

    public void createCube(int i, int j, int k, short data[][][], List<Vector3f> vertice, List<Vector2f> texcoord, List<Integer> indices, List<Vector3f> normals) {

    if (data[j][k] != 0) {

    // check front

    if (k == (data[j].length - 1) || data[j][k + 1] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 1)));

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 1)));

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 1)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 1)));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,1));

    texcoord.add(new Vector2f(1,1));

    indices.add(offset + 2);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 1);

    indices.add(offset + 3);

    indices.add(offset + 2);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(0,0,1));

    }

    }

    // check back

    if (k == 0 || data[j][k - 1] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 0)));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,1));

    texcoord.add(new Vector2f(1,1));

    indices.add(offset + 2);

    indices.add(offset + 1);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 2);

    indices.add(offset + 3);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(0,0,-1));

    }

    }

    // check left

    if (i == 0 || data[j][k] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 1)));

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 0)));

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 1)));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,1));

    texcoord.add(new Vector2f(0,1));

    indices.add(offset + 2);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 1);

    indices.add(offset + 3);

    indices.add(offset + 2);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(-1,0,0));

    }

    }

    // check right

    if (i == (data.length - 1) || data[j][k] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 1)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 1)));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,1));

    texcoord.add(new Vector2f(1,1));

    indices.add(offset + 2);

    indices.add(offset + 1);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 2);

    indices.add(offset + 3);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(1,0,0));

    }

    }

    // check bottom

    if (j > 0 && data[j - 1][k] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 0)));

    vertice.add(new Vector3f((i + 0),(j + 0), (k + 1)));

    vertice.add(new Vector3f((i + 1),(j + 0), (k + 1)));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,1));

    texcoord.add(new Vector2f(1,1));

    indices.add(offset + 2);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 1);

    indices.add(offset + 3);

    indices.add(offset + 2);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(0,-1,0));

    }

    }

    // check top

    if (j == (data.length - 1) || data[j + 1][k] == 0) {

    int offset = vertice.size();

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 0)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 0)));

    vertice.add(new Vector3f((i + 0),(j + 1), (k + 1)));

    vertice.add(new Vector3f((i + 1),(j + 1), (k + 1)));

    texcoord.add(new Vector2f(0,0));

    texcoord.add(new Vector2f(1,0));

    texcoord.add(new Vector2f(0,1));

    texcoord.add(new Vector2f(1,1));

    indices.add(offset + 2);

    indices.add(offset + 1);

    indices.add(offset + 0);

    indices.add(offset + 1);

    indices.add(offset + 2);

    indices.add(offset + 3);

    for (int n = 0; n < 4; n++) {

    normals.add(new Vector3f(0,1,0));

    }

    }

    }

    }



    public void markTheSpot() {

    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    Geometry geom = new Geometry("Box", b);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");

    mat.setColor("Color", ColorRGBA.Red);

    geom.setMaterial(mat);

    rootNode.attachChild(geom);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //TODO: add update code

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }

    }

    [/java]
2 Likes