How does the "first" argument work that DrawArrays takes?

How does the “first” argument work that DrawArrays takes and how does it interact with the “offset” that BindVertexBuffer takes?

I know that VertexAttribFormat’s “relativeoffset” + BindVertexBuffer’s “offset” = the offset in basic machine units of the first element in the buffer for the attribute and buffer specified. This is according the OpenGL 4.4 spec.
No where does it say what the heck the DrawArrays “first” argument means. A wiki however says that it: “Specifies the starting index in the enabled arrays.”

In the GL spec I haven’t even found that much, I have found nothing on what first means. The only thing remotely coming close is “Elements first through first + count - 1 of each enabled non-instanced array are transferred to the GL”.

The terminology is a little confusing, yes.

Let’s say that you have a vertex buffer, with maybe 1mb (or whatever, the size isn’t important for this explanation) worth of vertex data in it. This buffer contains vertices for all of the meshes used by your program. Taking it from the top.

BindVertexBuffer specifies the starting position in this buffer to draw from. Where this is useful is if you have two different vertex formats in the same buffer. So you might have a position-only format, and a more heavyweight position/normal/texcoord format (maybe you decided to burn some extra memory in exchange for saving bandwidth on shadow passes), and storing them in the same buffer means that the driver may be able to detect that only the offset needs to change and optimize accordingly.

The offset in VertexAttribFormat is used in the second case: an interleaved vertex format. Assuming a standard 3-float position, 3-float normal and 2-float texcoord, these offsets would be 0, 12 and 24. So you’ve now defined the offsets from the starting position where each vertex attribute is at.

Where the “first” param comes in is that you may be drawing a mesh with 2 textures. Everything else is the same but the textures are different so you need to make 2 draw calls. The first 200 vertices are for texture 0, the next 100 are for texture 1, so your draw calls are:

glDrawArrays (GL_WHATEVER, 0, 200); // draw first 200 vertices
glDrawArrays (GL_WHATEVER, 200, 100); // draw next 100 vertices; the first 200 have already been drawn so don't draw them again

This is also useful for vertex streaming, where you only want to draw a subrange of the buffer each time but don’t want to respecify the entire vertex layout for each draw call. Here the pseudocode for a typical vertex batch might look something like:

SetupVertexFormat ();
first = 0;

while (DrawingBatches ())
{
    if (BatchWillOverflowBuffer (count))
    {
        OrphanBuffer ();
        first = 0;
    }

    AddVerticesToBuffer (count);

    glDrawArrays (GL_WHATEVER, first, count);
    first += count;
}