8
\$\begingroup\$

I'd like to draw a large number (multiple thousands) of simple meshes (each maybe... a maximum of 50 triangles, but even that is a very large upper bound), all of which are exactly the same.

As a simple brute force way, right now I'm just doing thousands of draw calls, where I just change the transformation matrices that the shaders need and then draw the same objects again.

Of course this is hideously slow, since (I'm guessing) there are too many draw calls for the driver and my PC to handle efficiently.

What can I do to make it faster?

\$\endgroup\$
2
  • 4
    \$\begingroup\$ I can't give you a complete answer, but the high-level answer is to use geometry instancing. \$\endgroup\$ Aug 3, 2013 at 23:12
  • \$\begingroup\$ Search google for "Geometry Instancing", maybe you can find something that helps you that way \$\endgroup\$
    – Luis W
    Aug 3, 2013 at 23:13

1 Answer 1

11
\$\begingroup\$

The solution to this is instancing. This tutorial explains a few methods of instancing. If you have ARB_draw_instanced and ARB_instanced_arrays, use them.

The general idea is to store all of your meshes' transforms in a separate buffer object and bind that to an attribute array that uses one "vertex" per instance via glVertexAttribDivisor.

If, after that, you're still not running as fast as you want to, you're probably vertex processor bound. Do frustum culling on the meshes, and preferably build a BVH to traverse instead of doing the cull on all the meshes independently.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ +1; with many thousands of objects you may also become CPU-bound on the transforms (just sending position and rotation in your per-instance buffer and computing the per-object matrix on the GPU can help here), and don't forget to watch how you update that dynamic vertex buffer! \$\endgroup\$ Aug 3, 2013 at 23:43
  • \$\begingroup\$ @Robert Rouhani: Thanks, I've researched and promptly implemented it. Now I can draw 5000 meshes with 20 tris each at 250 FPS. By the way, your suggested way seems a bit outdated. Since OpenGL 3.something geometry instancing is official part of the OpenGL core specification, no need for ARB_xxx anymore. I use a uniform buffer to store the matrices, and use glDrawElementsInstanced. Inside the vertex shader, there's a layout(std140) uniform matrices { mat4 array[5000]; } where I can access the relevant element via array[gl_InstanceID] \$\endgroup\$
    – TravisG
    Aug 4, 2013 at 2:56
  • 1
    \$\begingroup\$ @TravisG I tend to try and support older versions. I understand both those extensions have been promoted to core for a while, the extension string will still be present, however :). Also, using glVertexAttribDivisor (which is not deprecated or outdated by any means) has the benefit of not having an upper limit on instance count. Performance has been historically better than uniform buffers, but is probably the same or very similar now, and both ways get the job done. \$\endgroup\$ Aug 4, 2013 at 3:03
  • \$\begingroup\$ @Robert Rouhani: Mhm... I guess it's true. Someone with outdated drivers could support ARB_xxx but not official core OpenGl 3.x features. I'll take a look at glVertexAttribDivisor, thanks. \$\endgroup\$
    – TravisG
    Aug 4, 2013 at 17:05
  • \$\begingroup\$ @Robert Rouhani: I implemented both versions (using uniform buffers, and alternatively your suggested version where I use a vertex attribute that only changes once per instance), and the uniform buffer version is actually about twice as fast (4000 fps in release mode, vs ~2000 with glVertexAttribDivisor, the 250 fps number was only for CPU-side). \$\endgroup\$
    – TravisG
    Aug 5, 2013 at 23:08

You must log in to answer this question.

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