Sherlock Holmes
Sherlock Holmes

Reputation: 291

OpenGL: How does Vertex Buffer manage its memory?

I'm learning OpenGL and I'm trying to understand things properly. If my understanding is incorrect at any point, please correct me.

Introduction

So let's say we have a triangle. This triangle has its vertices. Let's say these vertices only have the position set - no color, not anything else. These vertices are passed to the shaders using a buffer - let's call it VB (VBO in tutorials).

The shaders are the following:

Vertex shader:

#version 330 core
layout (location = 0) in vec3 aPos;

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

Fragment shader:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

The VB is an non-formatted array of data. For example, if we wanted to pass 3 one-byte values to this buffer (0, 255, 16), the data would look like this:

00FFF0

However, the shaders do not know how to read the data, so we need to "instruct" them by telling them what is what. To do this we use Vertex Array Objects. Let's call our Vertex Array Object VA.

To pass data to the buffer, glBufferData is used. When calling the function like this:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

We inform OpenGL that we want to buffer sizeof(vertices) elements from array vertices to the buffer currently bound to GL_ARRAY_BUFFER for static drawing.

Then, we inform VA how to use the data like this:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

This way we tell VA to get 3 floating point values from active buffer at the offset 0 while not normalizing coordinates and set this data to the vertex attribute at location 0.

This way, the shaders finally get all the data they need to work and our triangle is drawn.

Question

However, what if we wanted to change one vertex after we've already passed the data to the buffer?

In my understanding, we'd need to call glBufferData the same way as before. But how does it influence the data that was originally in the buffer? Does it overwrite it?

If it does overwrite it, how do we pass another data, let's say colors, without overwriting the positions?

If it doesn't, how does VA know the data it's "pointing" to is no longer up to date?

Upvotes: 1

Views: 607

Answers (2)

user4442671
user4442671

Reputation:

In my understanding, we'd need to call glBufferData the same way as before. But how does it influence the data that was originally in the buffer? Does it overwrite it?

glBufferSubData() lets you override a subrange of a buffer, so you can use it to selectively update parts of one.

If it does overwrite it, how do we pass another data, let's say colors, without overwriting the positions?

The simplest way to do that is to structure your buffer so that it's composed of separate sequential buffers instead of being one big interleaved buffer.

It would look roughly like so (take note of the stride parameter set to 0, which tells the driver that the data is not interleaved):

size_t coord_start = 0;
size_t normal_start = coord_start + coord_data_len;
size_t color_start = normal_start + normal_data_len;

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)coord_start );
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)normal_start );
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)color_start );

This way, when you call glBufferSubData() to update the memory range that contains colors, you will be leaving the coord and normal data alone.

Alternatively, you can also create separate buffers for different attributes.

If it doesn't, how does VA know the data it's "pointing" to is no longer up to date?

This is where things get kinda tricky. On paper, that's the driver's problem and you shouldn't worry about it.

In practice, because the GPU runs in parallel from what is happening on the CPU, you can end up in a circumstance where updating a buffer that's currently being used can cause some slowdowns as the synchronization resolves itself.

Because of this, it's sometimes preferable to just create a brand new buffer and fill it "from scratch" instead of updating an existing one. This way, you can be confident that the buffer update process doesn't step on the toes of any rendering making use of the buffer. When it is better to sub-updates vs fresh buffers depends on a lot of factors though.

Upvotes: 1

Rabbid76
Rabbid76

Reputation: 211230

The buffer data can be updated with glBufferSubData (or Mapping). glBufferData creates a new data store with immutable size. Therefore, you cannot add additional data to a buffer. You must first create a buffer that is large enough.
However you can create a separate buffer for additional attributes.

Upvotes: 1

Related Questions