2
\$\begingroup\$

I'm just started learning OpenGL and this is my first project besides tutorials. I'm trying to load a huge enginering model:

enter image description here

The data is structured in a way that I thought I could use instancing for very good performance, because many items are simple primitives (cylinder, torus, cones, etc) with different transformation matrices. So I structured my data this way:

Primitive 1
  VAO
    VBO -> vertices
    VBO -> indices

    Instance 1.1
      VBO -> object color
      VBO -> transformation matrix

    Instance 1.2
      VBO -> object color
      VBO -> transformation matrix

    Instance 1.n
      VBO -> object color
      VBO -> transformation matrix


Primitive n
  VAO
    VBO -> vertices
    VBO -> indices

    Instance n.1
      VBO -> object color
      VBO -> transformation matrix

    Instance n.n
      VBO -> object color
      VBO -> transformation matrix

The model has ~50k unique primitives and ~120k instances, so in general each primitive has 2 instances. But in practice, some have 10 and some have only 1. I end up with 50k VAOs and then call dlDrawElementsInstanced for each VAO.

The model is being draw at only 3 fps. I gues it's because of the number of glDraw calls (50k for the triangles and 50k for the outlines). The shaders are very simple, not even lightning is being applied.

To be sure of that I changed the way I organize the buffers: I put everything in a single buffer, but to do that I had to pass the color and transformations matrices as vertex attributes to each vertex. I know this isn't right but I had to test if the problem was the number of glDraw calls. I couldn't even load the model this way because the buffers get way to big and I get out of memory. So I tested this theory on a smaller model that was taking 50ms to render in the instanced way. With a single VAO for everything and only a single glDrawElements call the model is taking less than 1ms to render.

I know that putting everything in a single VAO and passing the trasnformations for every vertex is not the correct way of doing this. Now I also know that instancing isn't the tool for this due the very few intances for each mesh. So the question is, what would be the correct way to setup the buffers to minimize the number of glDraw calls?

I think that it would be perfect to store all in a single VAO (so I could call glDrawElements a single time) but use something like the glVertexAttribDivisor used in instancing to inform the shaders when to use the next shaders. But not exactly because I had to manually inform when to use the next matrix, in way like glPrimitiveRestartIndex works.

I'm using OpenGL 3.3

\$\endgroup\$
  • \$\begingroup\$ Do you really need to draw all that? How about several different culling techniques, like frustum culling and occlusion culling? You need to get those draw calls down somehow. \$\endgroup\$ – Lasse Jul 1 '15 at 12:59
  • \$\begingroup\$ "pass the color and transformations matrices as vertex attributes to each vertex" so you don't use glVertexAttribDivisor? \$\endgroup\$ – snake5 Jul 1 '15 at 13:02
  • \$\begingroup\$ @Lasse Short answer: no. But the long answer is that this is my 1st project with openGL. I know I'm doing something wrong with all those draw calls, so I'd like to make this right. Then I'll implement frustum and occlusion, since I have to study and learn about them first. \$\endgroup\$ – RBasniak Jul 1 '15 at 13:05
  • \$\begingroup\$ @snake5 Only in instanced version. Is there any way to use it without instancing? \$\endgroup\$ – RBasniak Jul 1 '15 at 13:07
  • \$\begingroup\$ @RBasniak ah, well, "to each vertex" confused me but it's good that you use it, seems like it should work... it's hard to tell without the actual code though if there are no errors \$\endgroup\$ – snake5 Jul 1 '15 at 13:20
4
\$\begingroup\$

Your performance hit come from driver overhead (state changes). Having 50K VAOs is wayyyyy too much.

If all your models are the same format (for example the format x,y,z,r,g,b,a) then you should use one VAO and one VBO.

You can use something like glMultiDrawArraysIndirect for rendering large amounts of object and you can create commands (which require no OpenGL calls) and then dispatch them all in one go with one OpenGL call to glMultiDraw*Indirect.

Using glMultiDraw*Indirect means you can store all your vertex data in one VBO too.

You can also get a preformace win by using glBufferStorage & persistantly mapped buffers to dispatch you matrix transforms & colour to the GPU.

This presentation show how you can avoid a lot of the driver overhead, along with source code too!

Also this presentation might be more geared towards what you are trying to do.

\$\endgroup\$
  • \$\begingroup\$ Having 50K VAOs is not too much but drawing all of them separately is. The scene should be culled to determine which objects to draw to reduce draw calls by not drawing the objects that are not visible. Another optimization would be combining some of the meshes in one instead of instance-drawing them. It works well if they share same shader and textures. \$\endgroup\$ – Lasse Jul 1 '15 at 13:10
  • \$\begingroup\$ Even if you cull it, say you have 5 objects in your view with 500 primitves each. Thats 2500 primitves and each have a VAO. Therefore just to draw 5 objects in a scene would require 2500 state changes to OpenGL. State changes are the biggest problem when rendering a large amount of objects as it inccurs alot of driver overhead. This is why the latest OpenGL & Vulkan are moving towards getting "closer to the metal". \$\endgroup\$ – Soapy Jul 1 '15 at 13:43
  • 1
    \$\begingroup\$ @Soapy I didn't know about these IndirectDraws. Thank you for heading me to the right way. It seems to be the solution I need, going to study them now. Also if someone come across this thread in future, this link also has very good explanation on the subject: openglsuperbible.com/2013/10/16/the-road-to-one-million-draws Going to occlusion and frustum culling after this is implemented :) \$\endgroup\$ – RBasniak Jul 1 '15 at 13:50

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.