2
$\begingroup$

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?

$\endgroup$
1
$\begingroup$

There are various options, even with OpenGL 3.3 (over a decade old), that will depend on hardware factors, GPU, bus, etc., the driver-specific implementations, and finally, the sort of access patterns and frequency of updates to the VBO that the application will be making.

I'm not entirely clear what you are thinking in terms of when and how a vertex array object 'knows' it's out of date. The VAO has attribute array bindings to specific buffers - and when the application modifies a VBO, it does so through the GL API. Therefore, the GL is forced to synchronize these updates - and that means potentially stalling the pipeline, which is a problem. And because it's a complex problem, there are several solutions...


The first is just to call glBufferData again, and replace the data. That might be fine for infrequent changes or small buffers - again, it's too hard to give a one-size-fits-all solution.

A better strategy in a lot of cases is 'orphaning'. Effectively, call glBufferData again, but this time with data = NULL. This can be very effective, because the GL knows it doesn't have to retain that data. There is still some cost in calling glBufferData or glBufferSubData - this time with actual data; but it doesn't need to synchronize waiting for a partial update.

VBOs have been around since OpenGL 1.5, so it's a well-established technique that drivers have optimized for a long time. It's relatively simple to implement, and tends to have good results. In most VBO update code I've written, it tends to perform well over a wide range of platforms.

There's the idea of 'ping-pong' buffers (alternating VBOs). Update a buffer not currently in use, while another is being used as a source. When a frame is finished, swap the VBOs used for a particular vertex attribute stream. I don't know how effective this is with more recent GPUs.

As an alternative approach, there's glMapBuffer, and the corresponding glUnmapBuffer. The driver is responsible for presenting the VBO data in a (modifiable) region of memory for the application. When the application has updated the data, the 'unmap' call updates the VBO. There are obvious issues with DMA transfers to and from GPU memory. The GL is effectively stalled - the VBO can't be used without generating errors - until the memory is unmapped.


Then there are 'ranged' buffer updates, with finer control over synchronization. To be honest, I'm getting out of my depth at this point, and the link I referenced earlier provides a better background than I can.

$\endgroup$
0
$\begingroup$

The data sent to the GPU is formatted. The GPU has to be programmed to know what that format is. If you want to add more data to the vertex attributes then the entire pipeline has to be reprogrammed, this includes overwriting the vertex attributes, changing the shaders, and reprogramming the GPU to know the data sizes.

If you change just the data, the GPU will have no clue, and will happily process garbage.

The are of course other ways to get data to a shader such as the so called "shader buffer object". But that seems to be outside the scope of your question.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.