I've looked around on the web trying to find a good answer to this, but am unable to find one. Different forums mention driver bugs and the likes, but these discussions are usually almost 10 years old, so bugs should probably have been fixed by now. So, to the question!
I'm drawing a mesh that consists of 628 008 vertices, normals and texture coordinates. I also have indices for the mesh, but the indices are the worst kind of indices: they do not re-use vertices, but actually just list the vertices in order (or close to). So we have 628 008 indices as well. Yes, I can re-write this to re-use vertices, but that's not really where my question is going.
The mesh is drawn using VBOs (vertices, normals, texture coords) in a VAO, and two different shaders: one for texturing one part of the mesh, and another shader for another non-textured part. To do this, I'm using the OBJ format and material groups (exported from Blender). The mesh is split into material groups, describing with which material a batch of indices shall be drawn.
The VBOs are set up to be drawn as GL_STATIC_DRAW since it is never changed.
I then loop over the material groups and draw the range of indices described in each group, like:
for( const auto& group : mesh)
if( group should use texturing )
{
enable texturing shader with phong material
set texturing uniforms
set material stuff
set mvp, etc
}
else
{
enable phong material shader
set material stuff
set mvp, etc
}
draw(group)
}
There are 13 different groups, so the draw command will be called 13 times.
I've tried drawing the mesh in five different ways and the results are quite surprising.
- glDrawArrays.
- glDrawElements with indices fed using pointer to index array. (reffered to as CPU-side indices below)
- glDrawElements using GL_ELEMENT_ARRAY_BUFFER and offset fed to command
- glDrawRangeElements with indices fed using pointer to index array.
- glDrawRangeElements using GL_ELEMENT_ARRAY_BUFFER and offset fed to command
The [start, end] vertex indices fed to glDrawRangeElements are calculated when reading the mesh at start-up so they are correct and precise. This could be a performance problem otherwise, according to the spec.
The results in ms (measured on CPU side using C++11 timers, using 500 samples and calculated mean value, collecting 3 values since the first frame's timing is huge):
glDrawArrays:
Median: 8.0 Mean: 8.9 Max: 217.0 Total: 4285.0 Samples: 482
Median: 8.0 Mean: 8.8 Max: 43.0 Total: 4345.0 Samples: 494
Median: 8.0 Mean: 8.2 Max: 14.0 Total: 4125.0 Samples: 501
glDrawElements:
GPU Indices (ELEMENT_ARRAY):
Median: 34.0 Mean: 44.1 Max: 597.0 Total: 20388.0 Samples: 462
Median: 34.0 Mean: 33.9 Max: 63.0 Total: 16973.0 Samples: 500
Median: 33.0 Mean: 33.5 Max: 80.0 Total: 16797.0 Samples: 501
CPU Indices (pointer to CPU-side indices):
Median: 9.0 Mean: 10.3 Max: 236.0 Total: 5027.0 Samples: 488
Median: 9.0 Mean: 9.2 Max: 32.0 Total: 4581.0 Samples: 500
glDrawRangeElements:
CPU Indices (pointer to CPU-side indices):
Median: 9.0 Mean: 10.1 Max: 221.0 Total: 4640.0 Samples: 459
Median: 10.0 Mean: 9.6 Max: 22.0 Total: 4780.0 Samples: 500
Median: 10.0 Mean: 9.5 Max: 32.0 Total: 4774.0 Samples: 501
GPU Indices (ELEMENT_ARRAY):
Median: 35.0 Mean: 36.9 Max: 597.0 Total: 16101.0 Samples: 436
Median: 34.0 Mean: 35.0 Max: 55.0 Total: 17517.0 Samples: 500
Median: 39.0 Mean: 39.1 Max: 64.0 Total: 19448.0 Samples: 497
So my huge surprise is: How come GPU-side indices takes so much longer? The frame rate literally drops to less than half (from about 34 fps to 15 fps).
What can be the reason for this? My guess would have been that the performance timings should be in this order (since we have crappy indices and they aren't really re-using anything):
- glDrawArrays <- Fastest
- glDrawRangeElements (using ELEMENT_ARRAY)
- glDrawElements (using ELEMENT_ARRAY)
- glDrawRangeElements (using pointer to indices)
- glDrawElements (using pointer to indices) <- Slowest
Why do you think that the performance is so bad using ELEMENT_ARRAY_BUFFER bound to the VAO? Is this driver specific? Is it because there are so many indices and memory have to be swapped around in the driver?
Hardware: MacPro running Windows 8 Dual AMD FirePro D300, 2048 MB dedicated graphics memory