Sign in to follow this  
ChristianFrantz

Too many draw calls

Recommended Posts

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 -.-

Edited by burnt_casadilla

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites
phil_t    8084

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.

Edited by phil_t

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
kunos    2254

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.

Share this post


Link to post
Share on other sites
0r0d    1929

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?

Share this post


Link to post
Share on other sites
phil_t    8084

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.

Share this post


Link to post
Share on other sites
iMalc    2466

Sorry but I have to agree that this really does come down to a lack of understanding about how raster 3D graphics work.

 

You describe how to draw the scene, then leave the system to do its job as it sees fit. It is more declarative style.
It doesn't even draw the next frame over top of the old one anyway. It starts drawing the next frame whilst it's busy sending the previous frame to your monitor. These things are designed for performance!

Edited by iMalc

Share this post


Link to post
Share on other sites
0r0d    1929

Yeah. It follows the character model

If the camera moves then what's on-screen moves.  YOu cant just draw stuff once and forget about it.  That's not how it works.  You need to draw from the view of the camera with enough frequency to make it smooth to the player.

Share this post


Link to post
Share on other sites
0r0d    1929

So why am I getting such a low frame rate? If everything has to be redrawn anyway then there's another problem in my code that I'm not seeing

I have no idea.  The amount of time that a gpu takes to render a frame depends on a lot of things, most of which you havent detailed in your post.  You can render 40,000 of anything and have very high or very low framerates.

Share this post


Link to post
Share on other sites
kunos    2254

 

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?

 

 

it doesnt matter.. even with a fixed camera.. if you don't _DRAW_ stuff EVERY FRAME, it's NOT THERE.

Share this post


Link to post
Share on other sites
phil_t    8084

So why am I getting such a low frame rate? If everything has to be redrawn anyway then there's another problem in my code that I'm not seeing

 

You're making 40,000 draw calls every frame! The GPU can do a lot of stuff very fast, but pushing data from the CPU to the GPU is not one of them. Typically even a large AAA game would make no more than a few hundred draw calls in a frame. 40,000 is absolutely insane.

 

If you put all your cubes into one vertex buffer, you could get away with a single draw call per frame, and I'm sure your frame rate would be fine. Or you could use instancing.

 

But first, you should really find a book/website that teaches about the basic performance behaviors of modern GPUs.

Edited by phil_t

Share this post


Link to post
Share on other sites

How do I put all my cubes into one vertex buffer when I'm using the vertex buffer to create a single cube? That makes more sense to me when I can just store multiple cubes in one buffer and draw them as needed.

Edited by burnt_casadilla

Share this post


Link to post
Share on other sites
Andy474    694

 

 

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?

 

 

it doesnt matter.. even with a fixed camera.. if you don't _DRAW_ stuff EVERY FRAME, it's NOT THERE.

 

 

xD 100% correct tongue.png

 

The XNA Draw Procedure

1/ CLEAR THE GRAPHICS DEVICE (To a nice colour)
2/ Proceed to draw the programmers stuff.
3/ End Draw Call
Edited by Andy474

Share this post


Link to post
Share on other sites
0r0d    1929

 

 

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?

 

 

it doesnt matter.. even with a fixed camera.. if you don't _DRAW_ stuff EVERY FRAME, it's NOT THERE.

 

 

If by "frame" he means the simulation timestep then whatever he draws to the screen will stay there until the backbuffer is swapped.  If he literally had a non-moving scene, and the platform doesnt complain about not getting any buffer swaps, then yes he could just draw once and continue to update frames without re-rendering the cubes.

 

Or he could render the cubes to 2 different buffers and just swap those regularly to keep the OS happy if it needs it. 

 

Technically he only has to issue draw calls if they are needed, but usually they're needed every frame.

Share this post


Link to post
Share on other sites

What I gathered from my hours of googling is that I'd only need to redraw the buffer when something in it changes, but something in my head didn't click and I didn't realize I had 40,000 single buffers being drawn for 40,000 different cubes with over 300,000 vertices lol. It makes sense to me now though. I just thought all the drawing could be done at Initialization and I wouldn't have to deal with redrawing until input is recieved 

Share this post


Link to post
Share on other sites
0r0d    1929

You should probably find a good tutorial on how rendering works.  Trying to do graphics without properly understanding how the render pipeline and the gpu works is bound to go very badly for you.

Share this post


Link to post
Share on other sites
superman3275    2061

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

Do you not know what a game-loop is? This isn't even low-level stuff. Every library I've ever worked with involving gamedev (around three) all require a game loop.

Edited by superman3275

Share this post


Link to post
Share on other sites
0r0d    1929

What I gathered from my hours of googling is that I'd only need to redraw the buffer when something in it changes, but something in my head didn't click and I didn't realize I had 40,000 single buffers being drawn for 40,000 different cubes with over 300,000 vertices lol. It makes sense to me now though. I just thought all the drawing could be done at Initialization and I wouldn't have to deal with redrawing until input is recieved 

Normally you want to have the render loop running at some rate along with the update loop.  In most cases these happen in sync, but they can happen at different rates like for example if the drawing is slow but you want to keep a faster update frequency for physics or whatever.  If the scene is truly static, then technically you can just draw it once to the backbuffer and swap and then leave it there, but on at least some hardware it will choke if it doesnt get swaps every so often.

 

You might also have situations where a scene is static but only rendered as part of the bigger scene on screen... like if you have a monitor showing what's going on in another part of the level.  The image on that monitor can be a texture into which you drew that part of the level, but if that doesnt change then you dont have to update the texture... until something does happen.

Share this post


Link to post
Share on other sites

 

What I gathered from my hours of googling is that I'd only need to redraw the buffer when something in it changes, but something in my head didn't click and I didn't realize I had 40,000 single buffers being drawn for 40,000 different cubes with over 300,000 vertices lol. It makes sense to me now though. I just thought all the drawing could be done at Initialization and I wouldn't have to deal with redrawing until input is recieved 

Normally you want to have the render loop running at some rate along with the update loop.  In most cases these happen in sync, but they can happen at different rates like for example if the drawing is slow but you want to keep a faster update frequency for physics or whatever.  If the scene is truly static, then technically you can just draw it once to the backbuffer and swap and then leave it there, but on at least some hardware it will choke if it doesnt get swaps every so often.

 

You might also have situations where a scene is static but only rendered as part of the bigger scene on screen... like if you have a monitor showing what's going on in another part of the level.  The image on that monitor can be a texture into which you drew that part of the level, but if that doesnt change then you dont have to update the texture... until something does happen.

 

My map isn't that complicated though. It's pretty static, except for some destroying and building of blocks. I see what you're saying and it makes sense, but for now I just need to get the map drawn on the screen in game time. Physics don't apply here yet

Share this post


Link to post
Share on other sites
0r0d    1929

 

 

What I gathered from my hours of googling is that I'd only need to redraw the buffer when something in it changes, but something in my head didn't click and I didn't realize I had 40,000 single buffers being drawn for 40,000 different cubes with over 300,000 vertices lol. It makes sense to me now though. I just thought all the drawing could be done at Initialization and I wouldn't have to deal with redrawing until input is recieved 

Normally you want to have the render loop running at some rate along with the update loop.  In most cases these happen in sync, but they can happen at different rates like for example if the drawing is slow but you want to keep a faster update frequency for physics or whatever.  If the scene is truly static, then technically you can just draw it once to the backbuffer and swap and then leave it there, but on at least some hardware it will choke if it doesnt get swaps every so often.

 

You might also have situations where a scene is static but only rendered as part of the bigger scene on screen... like if you have a monitor showing what's going on in another part of the level.  The image on that monitor can be a texture into which you drew that part of the level, but if that doesnt change then you dont have to update the texture... until something does happen.

 

My map isn't that complicated though. It's pretty static, except for some destroying and building of blocks. I see what you're saying and it makes sense, but for now I just need to get the map drawn on the screen in game time. Physics don't apply here yet

 

 

Just have a game loop that updates and renders the scene based on the current camera.  That's the simplest setup and all you need.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this