Uploading multiple objects to GPU asynchronously - c++

I have a a bunch of 3D models from the stereo camera on the Curiosity rover driving around on Mars. The models are being loaded from disk, multiple models at the same time, asynchronously. Now I need to upload this bunch of models asynchronously to the GPU(at runtime) to prevent a stall in the rendering loop, which is happening right now.
The way a model is uploaded right now:
glGenVertexArrays(1, &_vaoID);
glGenBuffers(1, &_vbo);
glGenBuffers(1, &_ibo);
glBindVertexArray(_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(Vertex), _vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, location)));
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, tex)));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<const GLvoid*>(offsetof(Vertex, normal)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(int), _indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
And the way it is rendered:
glBindVertexArray(_vaoID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glDrawElements(_mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
Right now I am uploading about 20 models to the GPU each time in the renderloop(when there are models loaded from disk and ready to be uploaded to the GPU) and it's way to much, the application stalls for about 50-400ms depending on the amount of vertices/normals/indices for the models.
Ping ponging between VBOs(updating one, reading from one) will probably not work in the current pipeline since each model has a random amount of vertices/normals/indices and needs to be connected to one specific texture.
I'm looking for any solution improving the performance.
Edit 1
I have now successfully created pointers to my VBO and IBO, however I'm confused how I'm supposed to unmap the buffers when they are returned to the main thread. My first thought was to unmap the VBO and IBO individually like this:
`
for (int i = 0; i < _vertices.size(); i++) {
_vertexBufferData[i] = _vertices.at(i);
}
glUnmapBuffer(GL_ARRAY_BUFFER);
for (int k = 0; k < _indices.size(); k++) {
_indexBufferData[k] = _indices.at(k);
}
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
But i get an error saying that the buffer is already unbound or unmapped. Do I only have to do the first unmap?

The problem you describe is called Buffer Object Streaming.
In a few words, say you got a trigger that a specific model has to be loaded. Then:
Create the VAO and VBOs as you described, but without loading any data yet. You use glBufferStorage for this.
Map the buffer into your memory and launch a thread to fill that buffer with the data. The thread will do all the time consuming disk i/o and filling of the mapped memory region.
When the worker thread is done, notify the main thread.
In the main thread, once you got the notification, unmap the buffers and mark the VAO as loaded for subsequent rendering.
Obviously between 1 and 4 your main thread continues rendering as usual, without rendering that pending VAO.

Related

How do OpenGL buffers relate to the VAO?

I'm currently learning OpenGL, but I'm having some problems understanding how the different buffers relate to the VAO. In my following code, I'm creating one VAO and two buffers (VBO for the vertex positions and EBO for the vertex order). At this point, if I understood it correctly, there is no connection between the VAO, VBO and EBO. I basically just created one VAO and two buffers. Now with glBindVertexArray and glBindBuffer I tell the OpenGL state machine my currently used VAO and assign my created buffers to a specific buffer type. With glBufferData I then load my data into the buffers. As I understand it, the VAO is still empty at this point and only gets data loaded into with the glVertexAttribPointer function. Now, OpenGL interprets the Data from GL_ELEMENT_ARRAY_BUFFER and loads them into the VAO at index 0. With glEnableVertexAttribArray(0) I then specify that the data at index 0 from my VAO (the vertex positions) should be used in the rendering process.
unsigned int VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
In my main loop, I specify a shader for my triangles, bind a VAO for rendering and then draw the elements followed by swapping the front and back buffers.
glUseProgram(shader);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
But how exactly does OpenGL know the order from the vertices? I have only uploaded the vertex position data into my VAO, but not the order(element) data. Does glVertexAttribPointer perhaps take into account the currently bound data from the GL_ELEMENT_ARRAY_BUFFER when loading data into the VAO? What am I missing here?
Now, OpenGL interprets the Data from GL_ELEMENT_ARRAY_BUFFER and loads them into the VAO at index 0.
No. Well sort of, but not in the way you seem to be describing it.
Most buffer binding points bind a buffer to the OpenGL context. The GL_ELEMENT_ARRAY_BUFFER binding point is different. It attaches the buffer to the VAO that itself is bound to the context. That is, the act of calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER) itself is what creates an association between the VAO and the index buffer. This association has nothing to do with glVertexAttribPointer.
This also means that if you don't have a VAO bound to the context, you cannot bind anything to GL_ELEMENT_ARRAY_BUFFER.

Why does OpenGL buffer unbinding order matter? [duplicate]

This question already has answers here:
What is the role of glBindVertexArrays vs glBindBuffer and what is their relationship?
(5 answers)
Closed 1 year ago.
I'm in the process of learning OpenGL (3.3) with C++, I can draw simple polygons using Vertex Buffer Objects, Vertex Array Objects, and Index Buffers, but the way I code them still feels a little like magic, so I'd like to understand what's going on behind the scenes.
I have a basic understanding of the binding system OpenGL uses. I also understand that the VAO itself contains the bound index buffer (as specified here), and if I'm not mistaken the bound vertex buffer too.
By this logic, I first bind the VAO, then create my VBO (which behind the scenes is bound "inside" the VAO?), I can perform operations on the VBO, and it will all work. But then when I come to unbind the buffers I used after I'm done setting up things, it seems that I must unbind the VAO first, then the vertex & index buffers, so the unbinding occurs in the same order as the binding, and not in reverse order.
That is extremely counter intuitive. So my question is, why does the order of unbinding matter?
(to clarify, I am rebinding the VAO anyway before calling glDrawElements, so I don't think I even need to unbind anything at all. it's mostly for learning purposes)
Here's the relevant code:
GLuint VAO, VBO, IBO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0); //unbinding VAO here is fine
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//if I unbind VAO here instead, it won't render the shape anymore
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
}
I believe I might have figured it out. Looking at this unbinding order
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbinding VBO
glBindVertexArray(0); //unbinding VAO
clearly "ruins" my polygons, because unbinding the VBO while my VAO is still bound means the VBO, which is supposed to "contain" references to the buffers it uses, is now not bound to a vertex buffer anymore. It doesn't know about the buffers.
Unbinding the VAO first allows me to then unbind any buffers without affecting it. This would be the correct order.
glBindVertexArray(0); //unbinding VAO
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbinding VBO
OpenGL's binding system is a little hard to grasp, especially when different things depend on one another, but I think my explanation is correct. Hopefully this could help some other learners in the future.

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.

why is glVertexAttribDivisor crashing?

I am trying to render some trees with instancing. This is rather weird, but before sleeping yesterday night, I checked the code, and it was in a running state, when I got up this morning, it is crashing when I am calling glVertexAttribDivisor I haven't changed any code since yesterday.
Here is how I am sending data to GPU for instancing.
glGenBuffers(1, &iVBO);
glBindBuffer(GL_ARRAY_BUFFER, iVBO);
glBufferData(GL_ARRAY_BUFFER, (ml_instance->i_positions.size()*sizeof(glm::vec4)) , NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, (ml_instance->i_positions.size()*sizeof(glm::vec4)), &ml_instance->i_positions[0]);
And then in vertex specification--
glBindBuffer(GL_ARRAY_BUFFER, iVBO);
glVertexAttribPointer(i_positions, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(i_positions);
glVertexAttribDivisor(i_positions,1); // **THIS IS WHERE THE PROGRAM CRASHES**
glDrawElementsInstanced(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0,TREES_INSTANCE_COUNT);
I have checked ml_instance->i_positions, it has all the data that needs to render.
I have checked the value of i_positions in vertex shader, it is the same as whatever I have defined there.
I am little out of ideas here, everything looks pretty much fine. What am I missing?