18
\$\begingroup\$

While refreshing my mind on OpenGL ES, I came across glDrawArrays and glDrawElements.

I understand how they are used and sort of understand why they are different.

What I do not seem to understand is that, I fail to see how glDrawElements can save draw calls (saving draw calls is a description that is mentioned by most of the books I have read, hence my mentioning of it here).

Imagine a simple scenario in which I tried to draw a square using 2 triangles.

I would need to have a set of 6 vertices using glDrawArrays while using glDrawElements, I would need only 4 in addition to an index array that has 6 elements.

Given the above, here is what I do not understand:

  1. how could glDrawElements save draw calls, if it still needs to use the index array (6 indices, in case of a square) to index into my vertex array that had 4 elements (6 times)? Other words, does it mean glDrawElements would still need to have a total of 6 draw calls just like glDrawArrays?
  2. how would using glDrawElements save space, if one would still need to have 2 arrays, namely, one for the vertices and one for the indices?
  3. In the case of drawing a square from 2 triangles, for simplicity, how many draw calls does glDrawElements (4 items in vertex array and 6 items in index array) and glDrawArrays (6 items only in vertex array) need, individually?

Thanks.

\$\endgroup\$

1 Answer 1

23
\$\begingroup\$

Each glDraw* is a draw call.

1 glDrawArrays is 1 draw call.
1 glDrawElements is 1 draw call.

It doesn't matter (so far as draw call count is concerned) how many vertices or indices you use, 1 glDraw* is 1 draw call.

The simple cases of drawing quads (assuming the GL version you use hasn't removed quads) or triangles are not a good example for comparing these, because a list of quads or a list of triangles can be drawn with a single call to either, and glDrawArrays will appear more efficient because it doesn't have the additional overhead of indexing.

Let's take a slightly more complex case, but one which is representative of a more real-world scenario. Imagine that you have a model composed of multiple triangle strips and fans:

glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);

In this example, using glDrawArrays gives you a total of 6 draw calls for the model. However, both strips and fans can easily be converted to indexed triangles, so add indices and it becomes:

glDrawElements (GL_TRIANGLES, .....);

That's one draw call, for the entire model.


The advantages of glDrawElements over glDrawArrays are more than just memory saving, which is the naive way of measuring it. There is potential for memory saving where vertices can be reused, because an index is typically smaller than a vertex (so even if you have lots of indices on balance they'll be smaller), but other advantages include:

  • Reduced draw call count. Each draw call incurs some overhead from validating state, setting things up, etc, and much of this overhead happens on the CPU side. By reducing the number of draw calls we avoid much of this overhead.

  • Vertex reuse. This is much more than just memory-saving. Your GPU likely has a hardware vertex cache, where recently transformed vertices can be stored; if the same vertex comes in again, and if it's in the cache, it can be reused from the cache rather than having to transform again. Your hardware will check the cache by comparing indices, so in OpenGL terms the only way you get to use the cache is to use glDrawElements.


So to answer your specific questions:

  1. how could glDrawElements save draw calls...? In the example you use, it doesn't. Each has one draw call. As I discuss above, this example is a very bad one for comparing the two.

  2. how would using glDrawElements save space...? Because indices are smaller than vertices. A 16-bit index is two bytes, a position/color/texcoord vertex is 24 bytes. 6 vertices is 144 bytes, 4 vertices plus 6 indices is 108 bytes.

  3. In the case of drawing a square from 2 triangles...? One and one. A glDraw* call is one draw call, the number of vertices or indices used is irrelevant for this measurement. But I must re-emphasise, this is a very bad example for comparing the two.


Finally, and just to complicate matters a little, while glDrawElements with triangles is the optimal path on desktop GPUs, on mobile it might be very different. Some mobile GPUs might prefer glDrawArrays with GL_TRIANGLE_STRIP (you would add degenerate triangles to concatenate primitives in this case), and I haven't even touched on more advanced topics such as primitive restart or multi-draw.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks so much for your feedback; I am just taking a little time reading what you have shared. Just wanted to let you know I acknowledged your response. Thanks again. \$\endgroup\$
    – Unheilig
    Nov 19, 2016 at 2:26
  • \$\begingroup\$ In your example of converting 6 DrawTriangleFans and DrawTriangleStrips calls to 1 single DrawTriangles calls. Does this really improve some performance? To my understanding drawing a triangle strip comprised of 100 triangles is much more efficient than drawing 100 triangles individually, and the benefit would easily offset any penalty occurred from using 6 function calls rather than 1. \$\endgroup\$
    – Samuel Li
    Mar 4, 2019 at 19:15
  • \$\begingroup\$ @SamuelLi 6-to-1 won't be a huge improvement, it's merely intended to illustrate the principle. Also, and as I indicated, strips are typically not a performance win over indexed lists on post-1998 desktop hardware. Check the date on any document advising you to use strips. \$\endgroup\$ Mar 4, 2019 at 21:14
  • \$\begingroup\$ Got your point, thanks for clarification! @MaximusMinimus \$\endgroup\$
    – Samuel Li
    Mar 4, 2019 at 21:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .