Why do OpenGL buffers (and objects in general) work? - opengl

Here is what I understand about how OpenGL buffers work: Sending data from the CPU to the GPU is slow, so we don't want to send, say, vertex data vertex by vertex. Instead, we send vertex data to the GPU all at once, and inform the GPU how to interpret what we send it. Buffers are involved in this process somehow (that's the point I do not understand).
So how to do this? We follow the OpenGL object creation model:
Generate a buffer ID
GLuint VBO;
glGenBuffers(1, &VBO);
Bind the buffer ID to a target buffer in the current state/context
glBindBuffer(GL_ARRAY_BUFFER, VBO);
Modify or query data. For instance, glBufferData() called on the target GL_ARRAY_BUFFER can be used to send the vertex data.
Set the target back to default
glBindBuffer(GL_ARRAY_BUFFER, 0);
De-allocate resources (edit: after the buffer has served its purpose; for instance, after the end of the game loop)
glDeleteBuffers(1, &VBO);
So at one hand I have the abstract idea of a block of GPU memory, and on the other hand I have a procedure to access that block, and I do not understand how those two correlate.

Related

OpenGL VAO with GL_DYNAMIC_DRAW

I am working with OpenGL and while developing I found this GL_DYNAMIC_DRAW buffers mode that states that buffer data may be changed, and I thought if I call glBufferData(...) while being bound to some VAO does it means that every time I will bing again this VAO all data that was in my memory will be automatically reuploaded to GPU?
Same idea in code:
data = [...] # List of arrays (actual data)
buffers = glGenBuffers(4) # Create buffers
vao = glGenVertexArrays(1) # Create VAO
glBindVertexArray(vao) # Bind VAO
for i, (buf, data) in enumerate(zip(buffers, data)):
glBindBuffer(GL_ARRAY_BUFFER, buf)
glBufferData(GL_ARRAY_BUFFER, data.nbytes, data, GL_DYNAMIC_DRAW)
glEnableVertexAttribArray(i)
glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, some_straid, ctypes.c_void_p(0))
glBindVertexArray(0) # unbind every thing ...
...
def on_render():
glClear(...) # clear screen
glUseProgram(...) # enable shader
glBindVertexArray(vao) # will this call upload new data to gpu?
glDrawArrays(GL_TRIANGLES, 0, 3) # draw
glBindVertexArray(0) # unbind VAO
glUseProgram(0) # unbind shader
glutSwapBuffers()
And if not, what is the best way to update those buffers (calling every time glBufferData?)?
Buffer usage:
usage is a hint to the GL implementation as to how a buffer object's data store will be accessed. This enables the GL implementation to make more intelligent decisions that may significantly impact buffer object performance. It does not, however, constrain the actual usage of the data store. usage can be broken down into two parts: first, the frequency of access (modification and usage), and second, the nature of that access. The frequency of access may be one of these:
For your specific case
DYNAMIC - The data store contents will be modified repeatedly and used many times.
If you want to update the buffer you have to call glBufferData or any of the other methods example: glBufferSubData, glMapBuffer. This has to be done when the buffer is bound ( Not the VAO, but the buffer )

Why are glDeleteBuffers and glDeleteVertexArrays so slow?

At some point in my program's flow I generate anywhere from between 0 and 300 meshes, each of them like so:
public Mesh(float[] vertices, byte[] indices, float[] textureCoordinates)
{
vao = glGenVertexArrays();
glBindVertexArray(vao);
vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(vertices), GL_STATIC_DRAW);
glVertexAttribPointer(ShaderProgram.VERTEX_ATTRIB, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(ShaderProgram.VERTEX_ATTRIB);
tbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, tbo);
glBufferData(GL_ARRAY_BUFFER, BufferUtils.createFloatBuffer(textureCoordinates), GL_STATIC_DRAW);
glVertexAttribPointer(ShaderProgram.TCOORD_ATTRIB, 2, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(ShaderProgram.TCOORD_ATTRIB);
ibo = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, BufferUtils.createByteBuffer(indices), GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
When the user presses a button these meshes need to be deleted again, so I run a cleanup method on each of these mesh objects:
public void cleanup()
{
glDeleteBuffers(vbo);
glDeleteBuffers(tbo);
glDeleteBuffers(ibo);
glDeleteVertexArrays(vao);
}
The problem is that I am trying to run at 60 fps and deleting 70 of these mesh objects takes about 30 ms (and deleting 110 of them takes 75 ms). This creates a noticable hiccup in performance because one frame should take at most ~16 ms.
Is this not the right way to dispose of VBO's and VAO's? I read in a different question (glDeleteBuffers slower than glBufferData) that
glGenBuffers and glDeleteBuffers are designed to only be run on initialization and cleanup, respectively. Calling them during runtime is bad.
but I am not sure how I can get rid of these VBO's and VAO's without calling the above functions.
I have thought of adding all meshes-to-be-deleted to a queue and only deleting a couple of them each frame, slowly emptying the queue, but that does not feel like the right solution. Another (possible) solution I have thought of is to use instanced rendering, but, as far as I understand, when I make sub 1000 draw calls per frame, non-instanced rendering should work fine too. My program will never have much more than 1000 Mesh objects at any given time, and I am not even sure this will solve my problem.
UPDATE: Besides from the below answer pointing me in exactly the right direction, I also discovered I wasn't actually deleting ~0-300 VBO's, but a factor of 48 more! No wonder performance was killed. So if anyone else ever has the same problem, thoroughly check the amount of glDeleteBuffers your code is performing.
You've hit some mental roadblock there. You seem to think that "per mesn: {one vertex attribute == one VBO}", but that's not how its supposed to work. What you should do is use one single, large VBO and use as a pool of memory from which you allocate chunks, each holding some data.
So you put not only all the vertex attributes of a single mesh into a common VBO, you also put several meshes into a single VBO.
Also it seems like you're creating and deleting your VBOs and VAOs on every single rendering iteration. Why? Do the meshes dramatically change between every frame? If that is so, that must be epilepsy inducing to watch; mesh deformations baked into the geometry data don't require recreation of the buffers, you just overwrite the data with glBufferSubData.

OpenGL VAO VBO resize

I have ran into an issue that uploading new content of a different size to the buffer causes VAO to behave unpredictably. Causing my object to look as if the buffer size was incorrectly set.
1) I generate VAO and VBO for an object
glGenVertexArrays(1, &_vao); // Generate 1 VAO
glGenBuffers(1, &_vbo); // Generate 1 VBO
2) Get location of uniform variables and attributes
3) Set the bindings like this:
glBindBuffer(GL_ARRAY_BUFFER, _vbo); // Bind VBO first
glBindVertexArray(_vao); // Bind VAO properties to VBO
{
GLsizei packSize = DIM * sizeof(GLfloat) * 2;
glEnableVertexAttribArray(_position);
glVertexAttribPointer(_position, DIM, GL_FLOAT, GL_FALSE, packSize, (GLvoid*)0);
// And so on ...
}
glBindVertexArray(0); // Unbind VAO
glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBO
4) Then I load my data:
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(float),
_vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
The problem starts when I try to load new data of a different size:
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(float),
_vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
And my buffer size seem to remain the same.
Though, if I define and bind my vao to vbo again (like in 3-d step)
glDeleteBuffers(1, &_vbo);
glGenBuffers(1, &_vbo);
// glBindBuffer... See step 3
// ...
It works.
Is it possible to avoid recreating the new buffer?
What is prefered way of resizing it?
Resize screenshot
Why it behaves such way?
I use OpenGL version 4.2.0 Build 10.18.10.3379
What is prefered way of resizing it?
The preferred way of resizing buffer objects is to not resize them. I'm being serious.
The OpenGL ARB created an entire alternate way of allocating storage for buffers, for the sole purpose of making it impossible to reallocate them later. OK, if you want to be technical, the purpose of immutable storage is to allow persistent mapping, which requires immutability, but if they just wanted persistent mapped buffers, they could have restricted the new API to just those.
Figure out what the maximum size of your data will be (or whatever maximum you want to support), allocate that up front, and just whatever part you want to.
Technically, your code ought to work. By the standard, if you resize a buffer object, things that reference it ought to be able to see the resized storage. But since implementations don't want you to resize them, high-performance applications don't resize them. So that code is rarely tested in real applications, and bugs can linger for quite some time.
Just don't do it.

OpenGL, VAOs and multiple buffers

I am writing a little graphics engine using OpenGL ( via OpenTK with C# ).
To define vertex attributes, I have a VertexDeclaration class with an array of VertexElement structures that are mapped to glEnableVertexAttribArray/glVertexAttribPointer calls.
Also, to support multiple vertex streams, I have a special structure holding a vertex buffer, vertex declaration, vertex offset and instance frequency (like the XNA's VertexBufferBinding structure).
Currently, whenever a drawing call is invoked, I iterate over all the set vertex streams and
bind their vertex buffers, apply vertex declarations, disable unused vertex attributes and draw the primitives.
I would like to use VAOs to cache the glEnableVertexAttribArray calls into them,
and whenever a vertex stream is applied, bind the VAO and change its array buffer binding.
Is that a correct usage of VAOs?
Is that a correct usage of VAOs?
No1.
glVertexAttribPointer uses the buffer object that was bound to GL_ARRAY_BUFFER at the moment the function was called. So you can't do this:
glVertexAttribPointer(...);
glBindBuffer(GL_ARRAY_BUFFER, bufferObject);
glDrawArrays(...);
This will not use bufferObject; it will use whatever was bound to GL_ARRAY_BUFFER when glVertexAttribPointer was originally called.
VAOs capture this state. So the VAO will, for each vertex attribute, store whatever buffer object was bound to GL_ARRAY_BUFFER when it was called. This allows you to do things like this:
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glVertexAttribPointer(0, ...);
glVertexAttribPointer(1, ...);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glVertexAttribPointer(2, ...);
Attributes 0 and 1 will come from buffer1, and attribute 2 will come from buffer2. VAO now captures all of that state. To render, you just do this:
glBindVertexArray(VAO);
glDraw*();
In short, if you want to change where an attribute's storage comes from in OpenGL, you must also change it's format. Even if it's the same format, you must call glVertexAttribPointer again.
1: This discussion assumes you're not using the new ARB_vertex_attrib_binding. Or, as it is otherwise known, "Exactly how Direct3D does vertex attribute binding." If you happen to be using an implementation that offers this extension, you can effectively do what you're talking about, because the attribute format is not tied with the buffer object's storage. Also, the tortured logic of glVertexAttribPointer is gone.
In general, the way we solve this in the OpenGL world is to put as many things as possible in the same buffer object. Failing that, just use one VAO for each object.

glDeleteBuffers slower than glBufferData

I'm having a bit of performance issue in my iOS/Android game where several VBO's have to be updated every once in a while. After profiling my game it turns out that glDeleteBuffers() takes up to 7ms per VBO update. This of course results in a hiccup when frames normally take only 4 ms to render.
Here's the part where I update my VBO:
Chunk* chunk;
pthread_join(constructionThread, (void**)&chunk);
building = false;
if (vboID)
{
//takes 7 milliseconds
glDeleteBuffers(1, &vboID);
vboID = 0;
}
if (offset)
{
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
//takes about 1-2 milliseconds, which is acceptable
glBufferData(GL_ARRAY_BUFFER, offset * 4, constructionBuffer, GL_STATIC_DRAW);
}
where offset is an instance variable is basically the size of the new VBO, which is quite variable. vboID speaks for itself, I guess ;)
glGenBuffers and glDeleteBuffers are designed to only be run on initialization and cleanup, respectively. Calling them during runtime is bad.
glBufferData replaces the current buffer data with a new set of data, which automatically changes the size of the buffer. You can safely remove the whole glGenBuffers/glDeleteBuffers thing and move it into initialization and cleanup.
Additionally, you are creating the buffer as a static buffer. This is telling OpenGL that you will almost never change it so it stores it in a way that's quicker to access on the GPU but slower to access from the rest of the system. Try changing GL_STATIC_DRAW to GL_DYNAMIC_DRAW or GL_STREAM_DRAW. More on this here: http://www.opengl.org/wiki/Buffer_Objects#Buffer_Object_Usage