Too many draw calls

Started by
28 comments, last by Alpha_ProgDes 10 years, 10 months ago

This has been a reoccurring problem for me and I gave up for a while. I tried fixing it again with no luck. What I did figure out is that I'm getting 320,000 draw calls a second, while drawing 40,000 cubes. I have no idea why this is happening either. I only need one draw call to draw my map, but it keeps getting redrawn for some reason.

This class is a list which stores the cubes from an array. The draw method is what I use in my main game class to draw the cubes.


public class DrawableList<T> : DrawableGameComponent
    {
        private BasicEffect effect;
        private ThirdPersonCam camera;

        public int numberOfCalls = 0;

        private class Entity
        {
            public Vector3 Position { get; set; }
            public Matrix Orientation { get; set; }
            public Texture2D Texture { get; set; }
        }

        private Cube cube;
        private List<Entity> entities = new List<Entity>();

        public DrawableList(Game game, ThirdPersonCam camera, BasicEffect effect)
            : base(game)
        {
            this.effect = effect;
            cube = new Cube(game.GraphicsDevice);
            this.camera = camera;
        }

        public void Add(Vector3 position, Matrix orientation, Texture2D texture)
        {

            entities.Add(new Entity()
            {
                Position = position,
                Orientation = orientation,
                Texture = texture
            });
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (var item in entities)
            {

                effect.VertexColorEnabled = false;
                effect.TextureEnabled = true;
                effect.Texture = item.Texture;

                Matrix center = Matrix.CreateTranslation(new Vector3(-0.5f, -0.5f, -0.5f));
                Matrix scale = Matrix.CreateScale(1f);
                Matrix translate = Matrix.CreateTranslation(item.Position);

                effect.World = center * scale * translate;
                effect.View = camera.view;
                effect.Projection = camera.proj;

                effect.FogEnabled = true;
                effect.FogColor = Color.CornflowerBlue.ToVector3();
                effect.FogStart = 1.0f;
                effect.FogEnd = 50.0f;

                cube.Draw(effect);
                numberOfCalls++;
            }

            base.Draw(gameTime);
        }
    }

This is my cube class. Pretty self explanatory.


public GraphicsDevice device;
    public VertexBuffer cubeVertexBuffer;

    public Cube(GraphicsDevice graphicsDevice)
    {
        device = graphicsDevice;

        var vertices = new List<VertexPositionTexture>();

        BuildFace(vertices, new Vector3(0, 0, 0), new Vector3(0, 1, 1));
        BuildFace(vertices, new Vector3(0, 0, 1), new Vector3(1, 1, 1));
        BuildFace(vertices, new Vector3(1, 0, 1), new Vector3(1, 1, 0));
        BuildFace(vertices, new Vector3(1, 0, 0), new Vector3(0, 1, 0));

        BuildFaceHorizontal(vertices, new Vector3(0, 1, 0), new Vector3(1, 1, 1));
        BuildFaceHorizontal(vertices, new Vector3(0, 0, 1), new Vector3(1, 0, 0));

        cubeVertexBuffer = new VertexBuffer(device, VertexPositionTexture.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly);

        cubeVertexBuffer.SetData<VertexPositionTexture>(vertices.ToArray());

        
    }

    private void BuildFace(List<VertexPositionTexture> vertices, Vector3 p1, Vector3 p2)
    {
        vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 1, 0));
        vertices.Add(BuildVertex(p1.X, p2.Y, p1.Z, 1, 1));
        vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 0, 1));
        vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 0, 1));
        vertices.Add(BuildVertex(p2.X, p1.Y, p2.Z, 0, 0));
        vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 1, 0));
    }

    private void BuildFaceHorizontal(List<VertexPositionTexture> vertices, Vector3 p1, Vector3 p2)
    {
        vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 0, 1));
        vertices.Add(BuildVertex(p2.X, p1.Y, p1.Z, 1, 1));
        vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 1, 0));
        vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 0, 1));
        vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 1, 0));
        vertices.Add(BuildVertex(p1.X, p1.Y, p2.Z, 0, 0));
    }

    private VertexPositionTexture BuildVertex(float x, float y, float z, float u, float v)
    {
        return new VertexPositionTexture(new Vector3(x, y, z), new Vector2(u, v));
    }

    public void Draw(BasicEffect effect)
    {

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Apply();
            device.SetVertexBuffer(cubeVertexBuffer);
            device.DrawPrimitives(PrimitiveType.TriangleList, 0, cubeVertexBuffer.VertexCount / 3);
        }
    }

Now, I realize that having two draw methods may be odd, but I didn't know how else to get this to work. I also realize that because the SetVertexBuffer is in my draw method, it's going to get called every time draw is called. What I don't understand is how to create one vertex buffer and set it just ONCE, at initialization, so that my fps isn't at 15.

Eventually, my goal is to have each 50x50 chunk being drawn only while it's in view of the camera, and this means storing each chunk into its own vertex buffer. Another thing I have no clue how to accomplish lol.

Another option would be to only draw the faces that are being shown, but again I don't know how this is done and it'd be useless to do until I can figure out I'm getting so many draw calls -.-

If you see a post from me, you can safely assume its C# and XNA :)

Advertisement

320k per SECOND or per FRAME?

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

Per second. The variable I'm using to count how many there are is in my first class in the draw method, so each time it's called I'm increasing the variable. But if I coded this correctly, shouldn't I only have the initial 320,000 calls without the number growing?

If you see a post from me, you can safely assume its C# and XNA :)


Per second. The variable I'm using to count how many there are is in my first class in the draw method, so each time it's called I'm increasing the variable. But if I coded this correctly, shouldn't I only have the initial 320,000 calls without the number growing?

If you're drawing 40,000 cubes with 40,000 draw calls, and you have 320,000 draw calls per second, all that tells me is that you're only getting 8 frames per second! (Which is not surprising).


I only need one draw call to draw my map, but it keeps getting redrawn for some reason.

Are you drawing them to a render target and then using that in subsequent frames or something? What are you doing to stop the cubes from being rendered after the first frame?

Do you understand how a game loop works? The screen is erased and everything is redrawn every frame.

I'm not doing anything to stop them from being drawn after the first frame. My plan was to redraw the cubes only when it was changed somehow, not every frame. I'm probably wrong in the way I'm thinking lol. I'm not too familiar with the low-level 3d stuff

If you see a post from me, you can safely assume its C# and XNA :)

I'm not too familiar with the low-level 3d stuff

clearly

"My plan was to redraw the cubes only when it was changed somehow, not every frame" is total nonsense and show a total lack of understanding about how raster 3D graphics work.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

Why redraw every frame when you only need to redraw if a cube is removed or placed? Doesn't make sense.

If you see a post from me, you can safely assume its C# and XNA :)

Why redraw every frame when you only need to redraw if a cube is removed or placed? Doesn't make sense.

It says there's a camera. Can the player move the camera around?

Yeah. It follows the character model

If you see a post from me, you can safely assume its C# and XNA :)

Generally games aren't static... either the camera is moving and/or objects are moving and passing on top of each other - every single frame. Modern hardware is made for drawing massive amounts of stuff every frame.

You can certainly render parts of a scene to a render target and re-use that. But doesn't your camera move? Don't you have things other than cubes in your game? In that case, you'll need to redraw everything anyway.

This topic is closed to new replies.

Advertisement