Vertex Specification Best Practices
Vertex Buffer Objects (aka VBOs) are an OpenGL abstraction which represents a server-side (AGP/PCIx or GPU) buffer into which vertex attribute and glDrawElements index arrays can be stored.
VBOs are defined by the ARB_vertex_buffer_object extension. This extension allows us to store vertex attribute arrays and/or the index lists for draw calls (such as glDrawElements/glDrawArrays/etc.) in fast AGP/PCIx or GPU memory so that they need not traverse the AGP/PCIx bus every frame. It essentially allows the developer to turn client-side (CPU) vertex attribute and index arrays into server-side (GPU) arrays. It provides the greatest speedup when some/all vertex attribute arrays are static or do not change every frame and thus can be pushed once and left on (or close to) the GPU.
For more details, see the Using Vertex Buffer Objects (NVidia whitepaper).
- VBO = Vertex Buffer Object VBO, a GL 1.5 feature
- IBO = Index Buffer Object. Not a real OpenGL object type, but developer lingo for a VBO used to contain glDrawElements indices instead of vertex attributes
Size of a VBO/IBO
- How small or how large should a VBO be?
You can make it as small as you like but it is better to put many objects into one VBO and attempt to reduce the number of calls you make to glBindBuffer and glVertexPointer and other GL functions.
You can also make it as large as you want but keep in mind that if it is too large, it might not be stored in VRAM or perhaps the driver won't allocate your VBO and give you a GL_OUT_OF_MEMORY.
1MB to 4MB is a nice size according to one nVidia document. The driver can do memory management more easily. It should be the same case for all other implementations as well like ATI/AMD, Intel, SiS.
Vertex, normals, texcoords
- Should you create a separate VBO for each? Would you lose performance?
If your data is static, then make 1 VBO for best performance. Be sure to interleave your vertex attribute data in the VBO and make the data block for each vertex a multiple of 32 bytes for good cache line coherence. See the other VBO page because it explains these details.
If one of the vertex attributes is dynamic, such as the vertex positions, you could store this in separate VBO.
By dynamic, we mean that you will be updating the VBO every frame. Perhaps you want to compute the new vertices on the CPU. Perhaps you are doing some kind of water simulation. etc.
No, you don't lose much performance if you use separate VBOs. It would be on the order of 5% but your testing might show otherwise.
//Binding the vertex glBindBuffer(GL_ARRAY_BUFFER, vertexVBOID); glVertexPointer(3, GL_FLOAT, sizeof(float)*3, NULL); //Vertex start position address //Bind normal and texcoord glBindBuffer(GL_ARRAY_BUFFER, otherVBOID); glNormalPointer(GL_FLOAT, sizeof(float)*6, NULL); //Normal start position address glTexCoordPointer(2, GL_FLOAT, sizeof(float)*6, sizeof(float*3); //Texcoord start position address
- If the contents of your VBO will be dynamic, should you call glBufferData or glBufferSubData (or glMapBuffer)?
If you will be updating a small section, use glBufferSubData. If you will update the entire VBO, use glBufferData (this information reportedly comes from a nVidia document). However, another approach reputed to work well when updating an entire buffer is to call glBufferData with a NULL pointer, and then glBufferSubData with the new contents. The NULL pointer to glBufferData lets the driver know you don't care about the previous contents so it's free to substitute a totally different buffer, and that helps the driver pipeline uploads more efficiently.
Another thing you can do is double buffered VBO. This means you make 2 VBOs. On frame N, you update VBO 2 and you render with VBO 1. On frame N+1, you update VBO 1 and you render from VBO 2. This also gives a nice boost in performance for nVidia and ATI/AMD.