I don’t think using VAOs has gained performance wise in the past, but I haven’t tested it recently, so the “fast path” might have changed.

If you’re changing quite a few attributes, then using VAOs should theoretically be better than manually doing it, since drivers could cache whether the VAO is valid, whereas every time you call glVertexAttribPointer it will need to do a number of checks.

AFAIK orphaning the vertex array object doesn’t invalidate the attribute pointers, they still point to the same offset in the buffer object as they did before.

If all your models are sharing a shared vertex structure, then I would use one VAO with all the attributes pointers pointing to the start of the streaming buffer object, and use the same VAO the whole time, just adjusting the enabled/disabled attributes without switching the VAO.

If you were to have models with different vertex layout, then switching between different VAOs each pointing to the start of the buffer object may be a good idea, making sure that if the vertex sizes are different, that you increment the current vertex offset to be a multiple of the new vertex size.

Something like this (written in notepad, untested etc.):

```
// -------------------- set up -------------------
// set up VBO + IBO
glBindBuffer(GL_ARRAY_BUFFER, streamingVertices);
glBufferData(GL_ARRAY_BUFFER, streamingVerticesSize, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, streamingIndices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, streamingIndicesSize, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// set up VAO
glBindVertexArray(streamingVAO);
glBindBuffer(GL_ARRAY_BUFFER, streamingVertices);
// position
for (int i=0; i<vao.num_attributes; i++)
glVertexAttribPointer(vao.attribute[i].index, vao.attribute[i].num_elements, vao.attribute[i].data, vao.vertexsize, BUFFER_OFFSET(vao.attribute[i].offset));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, streamingIndices);
glBindVertexArray(0);
// -------------------- draw time -------------------
// on begin streaming draw
glBindVertexArray(streamingVAO);
glBindBuffer(GL_ARRAY_BUFFER, streamingVertices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, streamingIndices);
int current_vertex = 0;
int current_index = 0;
int last_vao = -1;
int vertex_size;
int index_size;
for (int i=0; i< num_dynamic_meshes; i++)
{
if (last_vao != dynamic_mesh[i].vao)
{
current_vertex = increment_vertex_to_valid_position(current_vertex, last_vao, dynamic_mesh[i].vao);
current_index = increment_index_to_valid_position(current_index, last_vao, dynamic_mesh[i].vao);
glBindVertexArray(dynamic_mesh[i].vao);
last_vao = dynamic_mesh[i].vao;
vao = Get_VAO(dynamic_mesh[i].vao);
vertex_size = vao->vertex_size; // assuming nice size, eg. 64
index_size = vao->index_size;
index_type = convert_to_gl_index_type(vao->index_type);
}
int num_vertices = dynamic_mesh[i].num_vertices_to_be_generated();
int num_indices = dynamic_mesh[i].num_indices_to_be_generated();
if ((current_vertex + num_vertices)*vertex_size > streamingVerticesSize)
{
// orphan vertex buffer
glBufferData(GL_ARRAY_BUFFER, streamingVerticesSize, NULL);
current_vertex = 0;
}
if ((current_index + num_indices)*index_size > streamingIndicesSize)
{
// orphan index buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, streamingIndicesSize, NULL);
current_index = 0;
}
int vertex_offset = current_vertex * vertex_size;
int index_offset = current_index * vertex_size;
int vertex_data_length = num_vertices * vertex_size;
int index_data_length = num_indices * index_size;
if (dynamic_mesh[i].wants_mapped_pointers)
{
vertex_t *vertices = (vertex_t*)glMapBufferRange(GL_ARRAY_BUFFER, vertex_offset, vertex_data_length, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | MAP_UNSYNCHRONIZED_BIT);
dynamic_mesh[i].fill_vertices(vertices);
glUnMapBuffer(GL_ARRAY_BUFFER);
if (dynamic_mesh[i].has_indices)
{
index_t *indices = (index_t*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, index_offset, index_data_length, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | MAP_UNSYNCHRONIZED_BIT);
dynamic_mesh[i].fill_indices(indices);
glUnMapBuffer(GL_ELEMENT_ARRAY_BUFFER);
}
}
else
{
dynamic_mesh[i].generate_vertices();
glBufferSubData(GL_ARRAY_BUFFER, vertex_offset, vertex_data_length, dynamic_mesh[i].vertex_data());
if (dynamic_mesh[i].has_indices)
{
dynamic_mesh[i].generate_indices();
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, index_offset, index_data_length, dynamic_mesh[i].index_data());
}
}
dynamic_mesh[i].set_enabled_attributes();
if (dynamic_mesh[i].has_indices)
glDrawRangeElementsBaseVertex(dynamic_mesh[i].mode, current_vertex, current_vertex + num_vertices, num_indices, index_type, current_index, current_vertex);
else
glDrawArrays(dynamic_mesh[i].mode, current_vertex, num_vertices);
current_vertex += num_vertices;
current_index += num_indices;
}
```