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\$
  • 4
    \$\begingroup\$ I can't give you a complete answer, but the high-level answer is to use geometry instancing. \$\endgroup\$ – Seth Battin Aug 3 '13 at 23:12
  • \$\begingroup\$ Search google for "Geometry Instancing", maybe you can find something that helps you that way \$\endgroup\$ – Luis W Aug 3 '13 at 23:13
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\$
  • 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\$ – Maximus Minimus Aug 3 '13 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 '13 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\$ – Robert Rouhani Aug 4 '13 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 '13 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 '13 at 23:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.