Uptading parts of data in a VBO

Started by
8 comments, last by 21st Century Moose 10 years, 2 months ago

Hi!

I'm currently trying to set up a function that renders a textured 2D quad using VBOs. The function changes the vertex data of the VBO every time it's called so that you can stretch it, but the texture data of the VBO doesn't change. In other words I want to be able to change the vertex data without having to change the texture data of the VBO.

When I store my VBO I have a struct that contains 4 GLfloats (VertexData2D), the first two GLfloats represent the vertex data and the last two GLfloats represent the texture data. My problem is that I can't find a way to only change the vertex data without having to change the texture data, since the entire VBO contains both vertex data and texture data.

This the rendering part in the function:


glBindTexture( GL_TEXTURE_2D, get_texture_id() );		//Set texture

    //Enable vertex and texture coordinate arrays
    glEnableClientState( GL_VERTEX_ARRAY );
    glEnableClientState( GL_TEXTURE_COORD_ARRAY );

glBindBuffer( GL_ARRAY_BUFFER, VertexDataBuffer );		//Bind vertex data

//Update vertex buffer data
glBufferSubData( GL_ARRAY_BUFFER, 0, 4 * 4 * sizeof(GLfloat), VData );	

//Set texture coordinate data
glTexCoordPointer( 2, GL_FLOAT, 4 * sizeof(GLfloat), (GLvoid*)offsetof( VertexData2D, TexCoord ) );		
//Set vertex data
glVertexPointer( 2, GL_FLOAT, 4 * sizeof(GLfloat), (GLvoid*)offsetof( VertexData2D, Position ) );		

//Draw quad using vertex data and index data
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, IndexBuffer );
glDrawElements( GL_QUADS, 4, GL_UNSIGNED_INT, NULL );

    //Disable vertex and texture coordinate arrays
    glDisableClientState( GL_TEXTURE_COORD_ARRAY );
    glDisableClientState( GL_VERTEX_ARRAY );

I first bind the correct texture and VBO, then update the VBO using


glBufferSubData() 

(the VBO is set with GL_DYNAMIC_DRAW). I update the VBO starting at index 0, VData is the VBO that's going to replace the current one. Each vertex has the size


4 * sizeof(GLfloat)

and since I'm rendering a quad, the stride results in


4 * 4 * sizeof(GLfloat)

It's in glBufferSubData() that the problem is, my VBO contains both the vertex data and texture data, but I only want to change the vertex data. glBufferSubData() overwrites the current VBO, but I only want it to overwrite parts of it. This is essentially how I want it to work:

Update memory equal to: 2 * sizeof(GLfloat)

Skip memory equal to: 2 * sizeof(GLfloat)

Repeat 3 more times

What should I do? Is there another function that can do this?

Thank you!

Advertisement

Is there something wrong with creating separate buffers for positions and texture coordinates? This could help, https://www.opengl.org/wiki/Vertex_Specification_Best_Practices#Vertex.2C_normals.2C_texcoords

Maybe something like this:


// bind, update and set the pointer for positions
glBindBuffer(GL_ARRAY_BUFFER, position_buffer);
glBufferSubData(...);
glVertexPointer(...)

// bind, update and set the pointer for texture coordinates
glBindBuffer(GL_ARRAY_BUFFER, texcoord_buffer);
glBufferSubData(...);
glTexCoordPointer(...);

// bind index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);

// Draw
glDrawElements(...);

Derp

Use glMapBuffer or use separate VBOs.

Thank you both! I solved the problem by splitting the VBO into 2 parts, one for vertecies and one for texture coordinates. I didn't really know you could do that.

Cheers!

For this kind of thing you're really better off using a vertex shader to manipulate your positions. The function that changes positions can be coded in GLSL (perhaps with a uniform or two to control it), with all of the vertex data remaining static.

Otherwise you can achieve a similar effect by loading a new modelview matrix that represents your stretching.

Whichever way you choose, you need to be aware that VBOs are not intended for this kind of partial update. You can certainly do it, but the best use of VBOs comes from either having completely static data in them, from replacing the entire set of data, or from appending to existing data up to the maximum size of the buffer object. But trying to brute-force their usage in the same way as if you were still using immediate mode is extremely unlikely to end well.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


But trying to brute-force their usage in the same way as if you were still using immediate mode is extremely unlikely to end well.

I feel like GLSL is a little too much for me at this point. Would you say that it's better to replace both the vertex data and the texture coordinates rather than partially update in this case when it comes to performance?

I think it should be fine to update the data directly, especially when you're rendering just 2D stuff. You can optimize it later if it really becomes a problem with the performance.

But yeah, when you have a lot of vertices per object, you should transform those with matrices instead. This way you don't have to modify the actual vertices at all.

Derp


But trying to brute-force their usage in the same way as if you were still using immediate mode is extremely unlikely to end well.

I feel like GLSL is a little too much for me at this point. Would you say that it's better to replace both the vertex data and the texture coordinates rather than partially update in this case when it comes to performance?

No, I'd say it's better to profile and see which approach is fastest in your own program.

As a general guideline however, (1) interleaved data is going to be faster than non-interleaved data, and (2) updating all of a contiguous region in a VBO is going to be faster than jumping around to update smaller non-contiguous regions. Using non-interleaved data can help you avoid (2) (as you've discovered) but then you get bitten by (1).

To be honest, there are still some situations where not even using VBOs at all may be the best approach, and it looks as though you've got one here. If you've just got a single quad in that VBO, and if you need to update the data dynamically each frame, then glBegin/glEnd may even be faster. Remember - Quake used glBegin/glEnd and didn't suffer too much from it, so while it may not be the most optimal, it's still not as slow as some people might lead you to believe.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Quake used glBegin/glEnd and didn't suffer too much from it, so while it may not be the most optimal, it's still not as slow as some people might lead you to believe.

You should rephrase it as "Quake used glBegin/glEnd with 90s GPU hardware if they had GPU hardware at all". :D

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Quake used glBegin/glEnd and didn't suffer too much from it, so while it may not be the most optimal, it's still not as slow as some people might lead you to believe.

You should rephrase it as "Quake used glBegin/glEnd with 90s GPU hardware if they had GPU hardware at all". biggrin.png

And it still runs nowadays.

The point is not to be a glBegin/glEnd lover, the point is to pick the appropriate solution for the problem you're trying to solve. Using buffer objects the wrong way will be slower.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement