Download

Interesting Draw Call Experiment Result

Hi everyone, today I realized something about draw calls. I have done some basic tests and results surprised me.

1- Firstly I put a sky with 1 mesh and 1 material so I had 2 draw calls. As expected.

2- And I put a cube static mesh from basics tab and draw call increased to 4 from 2. As expected. (1 for mesh and 1 for material)

3-Then I put a movable skylight. No any change. As expected.

4-When I put a movable directional light (with 1 cascade). Draw call increased to 6 from 4. This was surprised me because I did not expect that a shadow (from static mesh) brings 2 draw calls.

This is my first question. Is this usual? Is anyone here to inform us about that?

5-So I dug a bit more and I put another static mesh which has 2 materials. And it brings 4 draw calls without shadow. If I turn on shadows it brings 8 draw calls (4 calls for shadow.) Isn’t something wrong here?

6- And my last experiment was about skeletal meshes. I put a skeletal mesh which has 1 mesh and 1 materials. It brings 3 draw calls. (this was 2 calls on same static mesh) and when I turn on shadows it brings 5 calls (2 for shadow). Finally, I put another skeletal mesh which has 1 mesh and 2 materials. And it brings 6 draw calls without shadow. And 10 draw calls with shadow.

These situations were pretty weird to me and I wanted to share here. I hope there is someone here to keep us informed.

Static meshes can’t be draw without materials so each mesh section is 1 draw call. If Mesh has 10 sections then it’s 10 draw calls.
Shadow mapping basically draw every mesh from light point of view to depth map. So you get 1 draw call per mesh section.

Yes, this is how it should be but somehow the engine adds more draw calls. For example, a mesh with 2 materials without shadow should bring 3 draw calls. But engine calls 4. If I put a movable directional light it bring 4 additional draw calls. (total 8) Let’s say shadows call source is material not mesh. Then Shadow cost should be 2 calls. But it isn’t

Are you using Stat SceneRendering to get your metrics?

There is a significant amount of machinery when rendering the scene.

Anything from batching, shadows, culling all impact.

Any results you get from adding removing single meshes need to be taken with a grain of salt…

BTW - A material by itself is not a draw call. So a single mesh with 1 section and 1 material should add 1 draw call.

A directional light shadow on default settings uses multiple cascades. Each cascade will require the objects to be drawn into it.

If you’re interested in doing such low level dive into the draw calls, download and install RenderDoc, go into your editor plugins and enable the RenderDoc plugin. It will add a small icon at the top right of the viewport which you can press at any time to capture the current frame and open it in RenderDoc for examination. There you can see absolutely everything the GPU is doing to render the captured frame, step by step.

I’m almost positive that it’s two draw calls per mesh(single mesh+single material). One for geometry and one for the material. Dynamic shadows add on another draw call per mesh section. If you have a model with four parts to it and four materials, then you’d have eight draw calls. If that scene were also using a dynamic light, the count would go up to 12 draw calls(4 mesh+4 mat+4 shadow).

EDIT: Also, I think it’s an extra draw call per dynamic light. So if you had three dynamic lights, it would add on three draw calls per mesh section. Using the previous example, with three dynamic lights, you would end up with 4 mesh+4 mat+12 shadow=18 draw calls. I’ll have to double check later to make sure.

Perhaps you are using the term draw call differently?

Historically “Draw Call” often refers to a specific group of functions that are implemented by the underlying graphics api.
No doubt the term has been around for a long time and is vague and confusing.

OpenGL, Vulkan, Metal all implement “Draw Calls”.

In general, the draw call refers to sending a vertex buffer down the pipeline for rendering by the gpu.
These vertices will be rasterized and the resultant fragment will be rendered using the currently bound GPU state.
The current state refers to the currently bound set of textures and shaders.
NOTE - The calls that do the actual binding of gpu state are not typically referred to as “Draw Calls”.

Lets use some really old legacy OpenGL to illustrate ( there are much better ways to do this but this is simple to show )
Suppose you have a model with three parts to it and three textures.

Notice there are 3 calls to glDrawArrays. glDrawArrays is a “Draw Call” in OpenGL. It sends the buffer down the pipeline.
The other calls to glVertexPointer and glTexCoordPointer set state. These are not “draw calls”



 // Enable state
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

// Set your used arrays
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords.data());

// Draw first section
glDrawArrays(GL_TRIANGLES, 0, size); // 'size' is the number of your vertices.
glVertexPointer(3, GL_FLOAT, 0, vertices2.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords2.data());

// Draw second section
glDrawArrays(GL_TRIANGLES, 0, size); // 'size' is the number of your vertices.
glVertexPointer(3, GL_FLOAT, 0, vertices2.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords2.data());

// Draw third section
glDrawArrays(GL_TRIANGLES, 0, size); // 'size' is the number of your vertices.
glVertexPointer(3, GL_FLOAT, 0, vertices3.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords3.data());

// Reset initial state
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);



Hence 3 draw calls.

https://www.pcper.com/reviews/Editor…What-Can-It-Do

Draw call means that you draw some geometry with some shader. At least you need vertex shader but optionally there can be tesselation stages, geometry shader and pixel shader.
You cannot draw a material. Material is just high level abstraction for shaders. Shader is the program that is run for every vertice and pixel that those vertices produce. Shaders can’t be run without input.
So draw call are just number of the mesh sections plus the draw calls needed for shadow rendering. For single cascade directional shadows you get as many draw calls as sections.

Yeah, that’s right. It was late and I wasn’t thinking clearly. I was thinking of material slots aka mesh sections lol…

I’m still pretty sure about the part involving having multiple dynamic shadow casting lights though. So if you had a mesh with four sections and three dynamic lights influencing the mesh, you’d tack on 12 draw calls(single cascade). Which would take it up to 16 draw calls for the whole object. Unless it batches all of the lights together in a single call? I’d definitely have to test this out to be sure.

Lights cannot be batched. Shadow pass could render all the sections at once if all sections use same depth pass material. But I don’t think it does so. If material does not use WPO, masked blend mode or depth offset then material does not need custom depth pass shader. This is the most common case so it could be quite big saving for draw calls. Has to test this up.

Yeah, I tested it out. Each dynamic light adds in extra draw calls per mesh section. Each cascade level adds more as well.

Here are some results from a blank level. I made sure to remove the character model from the 3rd person BP. Put a blocking volume in for a floor and made a static mesh comprised on four sections. I also used a default cube as well.

With no lights and no model: 0 draw calls

Default cube and no lights: 2 draw calls
Default cube and one dynamic light+one cascade: 3 draw calls
Default cube and one dynamic light+two cascades: 3 draw calls
Default cube and one dynamic light+three cascades: 4 draw calls
Default cube and one dynamic light+four cascades: 4 draw calls

Default cube and three dynamic lights+four cascades: 6 draw calls
Default cube and six dynamic lights+four cascades: 9 draw calls

Four piece static mesh and no lights: 5 draw calls
Four piece static mesh and one dynamic light+one cascade: 6 draw calls
Four piece static mesh and one dynamic light+two cascades: 6 draw calls
Four piece static mesh and one dynamic light+three cascades: 7 draw calls
Four piece static mesh and one dynamic light+four cascades: 7 draw calls

Four piece static mesh and three dynamic lights+four cascades: 11 draw calls
Four piece static mesh and six dynamic lights+four cascades: 17 draw calls

The numbers sometimes fluctuate because it looks like it’s reusing information from previous frames in a cache. If I recall correctly, they implemented this in a few releases ago. If the static mesh hasn’t moved, the lights haven’t moved, and nothing has gotten in the way to disturb the shadows, then there’s no reason to update them.