Jump to content

  • Log In with Google      Sign In   
  • Create Account


Too many draw calls


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
29 replies to this topic

#1 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 23 June 2013 - 09:50 PM

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, 23 June 2013 - 09:57 PM.

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


Sponsor:

#2 kunos   Crossbones+   -  Reputation: 2179

Like
0Likes
Like

Posted 23 June 2013 - 11:18 PM

320k per SECOND or per FRAME?


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#3 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 23 June 2013 - 11:29 PM

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 :)


#4 phil_t   Crossbones+   -  Reputation: 3156

Like
0Likes
Like

Posted 24 June 2013 - 12:09 AM


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, 24 June 2013 - 12:10 AM.


#5 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 12:19 AM

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 :)


#6 kunos   Crossbones+   -  Reputation: 2179

Like
-1Likes
Like

Posted 24 June 2013 - 12:30 AM

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
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#7 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 12:42 AM

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 :)


#8 0r0d   Members   -  Reputation: 797

Like
1Likes
Like

Posted 24 June 2013 - 01:18 AM

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?



#9 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 01:19 AM

Yeah. It follows the character model

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


#10 phil_t   Crossbones+   -  Reputation: 3156

Like
0Likes
Like

Posted 24 June 2013 - 01:21 AM

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.



#11 iMalc   Crossbones+   -  Reputation: 2258

Like
0Likes
Like

Posted 24 June 2013 - 01:21 AM

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, 24 June 2013 - 01:22 AM.

"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

#12 0r0d   Members   -  Reputation: 797

Like
0Likes
Like

Posted 24 June 2013 - 01:22 AM

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.



#13 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 01:24 AM

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

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


#14 0r0d   Members   -  Reputation: 797

Like
0Likes
Like

Posted 24 June 2013 - 01:25 AM

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.



#15 kunos   Crossbones+   -  Reputation: 2179

Like
0Likes
Like

Posted 24 June 2013 - 01:34 AM

 

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.


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#16 phil_t   Crossbones+   -  Reputation: 3156

Like
1Likes
Like

Posted 24 June 2013 - 01:43 AM


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, 24 June 2013 - 01:47 AM.


#17 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 01:44 AM

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, 24 June 2013 - 01:46 AM.

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


#18 Andy474   Members   -  Reputation: 651

Like
0Likes
Like

Posted 24 June 2013 - 01:57 AM

 

 

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, 24 June 2013 - 01:58 AM.


#19 0r0d   Members   -  Reputation: 797

Like
0Likes
Like

Posted 24 June 2013 - 01:59 AM

 

 

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.



#20 burnt_casadilla   Members   -  Reputation: 423

Like
0Likes
Like

Posted 24 June 2013 - 02:02 AM

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 


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





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS