When should I call glDeleteBuffers()?
What you're seeing is well defined behavior. The following are the key parts of the spec related to this (emphasis added).
From section "5.1.2 Automatic Unbinding of Deleted Objects" in the OpenGL 4.5 spec:
When a buffer, texture, or renderbuffer object is deleted, it is unbound from any bind points it is bound to in the current context, and detached from any attachments of container objects that are bound to the current context, as described for DeleteBuffers, DeleteTextures, and DeleteRenderbuffers.
and "5.1.3 Deleted Object and Object Name Lifetimes":
When a buffer, texture, sampler, renderbuffer, query, or sync object is deleted, its name immediately becomes invalid (e.g. is marked unused), but the underlying object will not be deleted until it is no longer in use.
A buffer, texture, sampler, or renderbuffer object is in use if any of the following conditions are satisfied:
the object is attached to any container object
...
The VAO is considered a "container object" for the VBO in this case. So as long as the VBO is referenced in a VAO, and the VAO itself is not deleted, the VBO stays alive. This is why your version of the code with the glDeleteBuffers()
at the end works.
However, if the VAO is currently bound, and you delete the VBO, it is automatically unbound from the VAO. Therefore, it is not referenced by the VAO anymore, and deleted immediately. This applies to the case where you call glDeleteBuffers()
immediately after glVertexAttribPointer()
.
In any case the id (aka name) becomes invalid immediately. So you would not be able to bind it again, and for example modify the data.
There are some caveats if you dig into the specs more deeply. For example, if you delete a buffer, and it stays alive because it is still referenced by a VAO, the name of the buffer could be used for a new buffer. This means that you basically have two buffers with the same name, which can result in some confusing behavior.
Partly for that reason, I personally wouldn't call glDelete*()
for objects that you want to keep using. But others like to call glDelete*()
as soon as possible.