Thursday, January 14, 2016

xna - Limit the number of draw calls


I know where the error lies in my code at this point, and I know what I need to do to fix it, I just don't know how. My problem comes with the 120,000+ draw calls I get each frame. This is caused by my main method which draws each cube in a list cubes.


     public void DrawMap(GameTime gameTime)
{
foreach (Cube block in cube.cubes)
{

Matrix translate = Matrix.CreateTranslation(block.cubePosition);
cube.Draw(effect);

}
}

Now obviously there is more than just this in the method, but these are the two lines I'm focused on. I need the translate matrix to place cubes where they're supposed to go, and the cube.Draw for actually drawing the cube. My goal was to create a chunk of cubes to limit the number of draw calls, but what I have isn't accomplishing that.


What I was thinking is that I need to just use the cube.Draw method. So I tried this


   public void Draw(BasicEffect effect)
{

device.SetVertexBuffer(vertexBuffer);
device.Indices = indexBuffer;

foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
no++;
pass.Apply();

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
}

}

There was more to it than that, but you get the idea. This only created one cube, although the index and vertex buffers have more than one cubes indices and vertices stored in it as illustrated by the code below seen in many of my past questions.


  for (x = 0; x < 50; x++)
{
for (z = 0; z < 50; z++)
{
for (y = 0; y <= map[x, z]; y++)
{
SetUpIndices();

SetUpVertices();
cubes.Add(new Cube(device, new Vector3(x, map[x, z] - y, z), grass));
}
}
}


vertexBuffer = new VertexBuffer(device, typeof(VertexPositionTexture), vertices.Count, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices.ToArray());


indexBuffer = new IndexBuffer(device, typeof(short), indices.Count, BufferUsage.WriteOnly);
indexBuffer.SetData(indices.ToArray());

What I learned through using a breakpoint is that there were actually 2500 cubes being drawn in one place. I had only about 150 draw calls a frame at that time, which is a lot better than what I did have.


Is there something I can change that sets the vertices and indices to the position of my cube in order to draw the cubes in the right place?


    private void SetUpVertices()
{
//front left bottom corner ok
vertices.Add(new VertexPositionTexture(new Vector3(0, 0, 0), new Vector2(1, 0)));
//front left upper corner

vertices.Add(new VertexPositionTexture(new Vector3(0, 1, 0), new Vector2(1, 1)));
//front right upper corner ok
vertices.Add(new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(0, 1)));
//front lower right corner
vertices.Add(new VertexPositionTexture(new Vector3(1, 0, 0), new Vector2(0, 0)));
//back left lower corner ok
vertices.Add(new VertexPositionTexture(new Vector3(0, 0, -1), new Vector2(0, 1)));
//back left upper corner
vertices.Add(new VertexPositionTexture(new Vector3(0, 1, -1), new Vector2(1, 1)));
//back right upper corner ok

vertices.Add(new VertexPositionTexture(new Vector3(1, 1, -1), new Vector2(1, 0)));
//back right lower corner
vertices.Add(new VertexPositionTexture(new Vector3(1, 0, -1), new Vector2(0, 0)));
}

private void SetUpIndices()
{
indices.Add(0);
indices.Add(2);
indices.Add(3);


indices.Add(0);
indices.Add(1);
indices.Add(2);

indices.Add(1);
indices.Add(5);
indices.Add(6);

indices.Add(1);

indices.Add(6);
indices.Add(2);

indices.Add(2);
indices.Add(6);
indices.Add(7);

indices.Add(2);
indices.Add(7);
indices.Add(3);


indices.Add(4);
indices.Add(7);
indices.Add(6);

indices.Add(4);
indices.Add(6);
indices.Add(5);

indices.Add(1);

indices.Add(4);
indices.Add(5);

indices.Add(1);
indices.Add(0);
indices.Add(4);

indices.Add(0);
indices.Add(7);
indices.Add(4);


indices.Add(0);
indices.Add(3);
indices.Add(7);

Answer



Your code for SetupVertices is not taking into account the position of each cube in the chunk. The vertex buffer is for the whole chunk - and individual vertices must be stored in "chunk" space (relative to to the position of the chunk). Right now you're just creating a bunch of cubes at the origin. You need to translate them (just use +) to the correct position.


Then your SetupIndices method is not taking into account the location in the vertex buffer where each cube starts. So, effectively, each time you're adding a cube, you're really telling the GPU to render the first cube in the buffer - and the later cubes are just being ignored.


Here is a modified version of your code. I haven't tested it or checked your numbers. This is just to illustrate how you must do the offsetting for both vertices and indices. This code replaces SetupVertices and SetupIndices with a single method that takes the coordinates of each cube relative to the chunk.


private void SetUpVerticesAndIndicies(int x, int y, int z)
{

short verticesStart = (short)vertices.Count;
Vector3 cubePosition = new Vector3(x, y, z);

// Create the appropriate vertices for the cube:

//front left bottom corner ok
vertices.Add(new VertexPositionTexture(cubePosition + new Vector3(0, 0, 0), new Vector2(1, 0)));
//front left upper corner
vertices.Add(new VertexPositionTexture(cubePosition + new Vector3(0, 1, 0), new Vector2(1, 1)));
//front right upper corner ok

vertices.Add(new VertexPositionTexture(cubePosition + new Vector3(1, 1, 0), new Vector2(0, 1)));
// ... etc ...

// Create the appropriate indices for the cube:
// (these are indexes into the vertices list)

indices.Add(verticesStart + 0);
indices.Add(verticesStart + 2);
indices.Add(verticesStart + 3);


indices.Add(verticesStart + 0);
indices.Add(verticesStart + 1);
indices.Add(verticesStart + 2);

// ... etc ...
}

Hopefully you can see how this would translate to a method to add individual faces, as we discussed in your previous questions.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...