1
\$\begingroup\$

I am currently developing a voxel game and I had recently noticed that the game from startup is at around 2GB of memory. Currently I am rendering 14 x 14 chunks as shown: Terrain: 14 x 14 Chunks

I did a profile on the game and I noticed that the function call glfwSwapBuffers(...) takes 9,964 m/s to run (76.4%) and as the program's life continued it kept growing. The next function is glDrawElements() which takes 849 m/s to run (8.49%). I am unsure what is causing the bottleneck.

Going back to the high memory usage issue there are two things that seem to be causing high memory. As explained in this thread here: Voxel Game - Lag when camera is inside of the terrain I generate the terrain once and since I posted that question, I create a mesh for each chunk made up of the visible vertices. After that I send each chunk entity to my renderer and it is drawn from these two blocks of code.

    for(Chunk[] chunks1D: chunks)
    {
        for(Chunk chunk: chunks1D)
        {
            //Model matrix
            Matrix4f modelViewMatrix = transformation.getModelViewMatrix(chunk.getEntity(), viewMatrix);
            shader.setUniform("modelViewMatrix", modelViewMatrix);
            //Render mesh
            shader.setMaterialUniform(chunk.getEntity().getMesh().getMaterial());
            chunk.getEntity().render();
        }
    }

which calls this

public Matrix4f getModelViewMatrix(Entity entity, Matrix4f viewMatrix)
{
    Vector3f rotation = entity.getRotation();
    Vector3f position = entity.getPosition();
    float scale = entity.getScale();
    modelMatrix.identity().translate(position).
        rotateX((float)Math.toRadians(-rotation.x)).
        rotateY((float)Math.toRadians(-rotation.y)).
        rotateZ((float)Math.toRadians(-rotation.z)).
        scale(scale);
    //Copy view matrix
    Matrix4f currentView = new Matrix4f(viewMatrix);
    modelViewMatrix = currentView.mul(modelMatrix);
    return modelViewMatrix;
}

and this

public void render()
{

    //Bind the VAO
    glBindVertexArray(vaoID);
    glEnableVertexAttribArray(0); //Vertex Data
    glEnableVertexAttribArray(1); //Colour data
    glEnableVertexAttribArray(2); //Normal data

    //Draw the vertices: mode: Triangles, count, type: integers, offset
    glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_INT, 0);

    //Restore state
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
    glBindVertexArray(0);

}

From the profile I found two sources of high memory, one for Matrix4f which is due to the getModelViewMatrix(...) function (the memory usage grows over the programs lifespan) and another for float and float[] from the genChunkMesh(...) function as shown in my other thread (Voxel Game - Lag when camera is inside of the terrain) which is called once when a chunk is loaded. It seems as if for both cases that Matrix4f, float[] and float instances are being created by they are never released from memory. :/ I have tried looking everywhere for a solution but I can't find one... I feel my game is optimised well enough for what is being rendered and these problems shouldn't be occurring.

tldr: Simple voxel terrain gen requires high amounts of CPU and the culprit is glfwSwapBuffers which takes 9,694 m/s to run (grows over time). High memory usage also from Matrix4f, float and float[] (floats of which seem to be retained from chunk mesh generation) objects causing the game to use ~2Gb of memory from startup (14 x 14 chunks) each chunk contains its own mesh (containing a VAO which has all of the chunks visible vertices, normals and colours) and two 3D int arrays one for the chunk's blocks and one for the chunk's visible blocks (16 * 16 * 128) each storing the block ID at that location.

\$\endgroup\$
  • \$\begingroup\$ You shouldn't render invisible blocks \$\endgroup\$ – Bálint Jun 27 '17 at 6:55
  • \$\begingroup\$ Currently I have it so the chunk builds a mesh from only the visible block faces :/ \$\endgroup\$ – AtomProgrammer Jun 27 '17 at 6:56
2
\$\begingroup\$

First of all, we can say with absolute confidence that the culprit is not your glfwSwapBuffers call.

On it's own SwapBuffers doesn't do much; what it does however do is take all of the GL commands issued in the preceding frame and flush them to the GPU for execution. Because this happens within your SwapBuffers call, it only appears as though SwapBuffers is to blame; it most assuredly is not.

The cause of your problems is the amount of work you send to the GPU, and structuring your GL calls in a manner that allows your driver to optimize them should be your solution. Look at this for starters:

//Bind the VAO
glBindVertexArray(vaoID);
glEnableVertexAttribArray(0); //Vertex Data
glEnableVertexAttribArray(1); //Colour data
glEnableVertexAttribArray(2); //Normal data

//Draw the vertices: mode: Triangles, count, type: integers, offset
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_INT, 0);

//Restore state
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindVertexArray(0);

One of your problems here is that whether or not vertex attrib arrays are enabled or disabled are stored as part of the VAO state, so you do not need to enable them or disable them again. You're doing needless work, your VAO is incurring 2 changes per bind, and your driver cannot optimise this workload.

Let's make things a little bit better:

//Bind the VAO
glBindVertexArray(vaoID);

//Draw the vertices: mode: Triangles, count, type: integers, offset
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_INT, 0);

//Restore state
glBindVertexArray(0);

Now you've still got a problem here which is coming from the fact that you unbind the VAO. Because of this you're also incurring 2 VAO changes per draw call, and your OpenGL driver is not going to be nice and optimize them out. A much better loop looks like this:

glBindVertexArray (...);

for (...) {
    for (...) {
        glDrawElements (...);
    }
}

See the difference - now you're only incurring a single VAO change overall and this is a workload that your driver will be much better able to optimize for you.

I suggest that you make these changes first and then re-profile.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your help, this is my first 3D game! It is greatly appreciated! I will try your advice, I am a little stuck on the binding of the vao once and looping since I have one for each chunk \$\endgroup\$ – AtomProgrammer Jun 27 '17 at 9:21
  • \$\begingroup\$ I tried removing the glEnableVertexAttribArray(..) and glDisable... calls and it doesn't render anything now :( \$\endgroup\$ – AtomProgrammer Jun 27 '17 at 9:28
  • \$\begingroup\$ @AtomProgrammer add the glEnableVertexAttribArray calls to where you initialize the VAO \$\endgroup\$ – ratchet freak Jun 27 '17 at 9:52
  • \$\begingroup\$ Thank you, that works! Do I need to worry about disabling them again? Now I am a bit confused on how to do the for loop part as each chunk has its own VAO. \$\endgroup\$ – AtomProgrammer Jun 27 '17 at 10:00
  • \$\begingroup\$ No, don't worry about disabling them again. Which arrays are enabled are part of the VAO state so unless you explicitly wish to change the VAO state, you shouldn't. \$\endgroup\$ – Maximus Minimus Jun 27 '17 at 10:23
1
\$\begingroup\$

when building the mesh for each chunk you can use the neighbouring chunk to see whether a face needs to be drawn. This could lead to some chunks not needing to be drawn at all.

Can you try using separate vertex formats? That may be a bit faster using a single VAO and VBO binding.

That will make the Chunk.render() look something like:

render(){
    if(numVertices == 0) return;

    //Bind the VBO & EBO
    glBindVertexBuffer(vertexBindingPoint, vbo, vboOffset, VertexStride);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

    //Draw the vertices: mode: Triangles, count, type: integers, offset
    glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_INT, 0);
}
\$\endgroup\$
  • \$\begingroup\$ Thank you! I will try looking at the neighbouring chunks to remove more cube faces! I'll also give the trying separate vertex formats a go! \$\endgroup\$ – AtomProgrammer Jun 27 '17 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.