So what is the best way to render geometry that consists of an extremely large number of draw calls (10000-20000) but each only having up to 10 vertices?
That really depends on the usage case. There are many ways that a draw loop for such a list of objects could appear. For example, you have this:
for(Each Object)
{
Bind Object.Program
Set Object.Uniforms
Bind Object.Textures
Bind Object.VAO (buffer objects)
glDraw*
}
In this case, each object has a program, a set of uniforms and textures, a VAO (or, if you’re allergic to VAOs, the set of array object state), and a draw command.
This is the worst possible way to draw, well, anything. If this is your code, the very first thing you need to do is just some state sorting. Make it into something like this:
for(Each Program)
{
Bind Program
for(Objects using Program1)
{
Set Object.Uniforms
Bind Object.Textures
Bind Object.VAO (buffer objects)
glDraw*
}
}
This is the most basic of state sorting: sorting renderable things based on what program object they use. Minimizing the number of glUniform and glBindTexture calls that are made is the next step. This will require some data analysis on your part.
After that, you’re going to want to start sorting by VAO. This will of course mean that multiple objects will be sharing the same buffer object and vertex format. This shouldn’t be hard.
It changes the code to look like this:
for(Each Set)
{
Bind Set.Program if different from currently bound
Bind Set.VAO if different from currently bound
for(Objects in Set)
{
Set Object.Uniforms
Bind Object.Textures
glDraw*
}
}
Where a “Set” is a set of objects that all use the same Program and VAO.
Now, if you’ve got things optimized to where you are changing only the absolute bare minimum state between draw calls, then you’re ready to talk about actual optimizations for drawing.
If you are drawing lots of copies of the same object (or, depending on the case, small permutations of the same object that you could choose in a vertex shader) then instancing is a tool you’ll be interested in. It would allow you to do this:
for(Each Set)
{
Bind Set.Program if different from currently bound
Bind Set.VAO if different from currently bound
for(Objects in Set)
{
Add per-object state to the state buffer object or texture
}
Bind state buffer object or texture
glDraw*Instanced(#Objects in Set)
}
One draw call would draw all of the objects.
There are a lot of caveats when using instancing. If you want to go that route, do some investigating.