Skip to main content
GameDev.net gamedev.net
🔒 Locked

Stream VBO vs Vertex Array

Started by PumpkinPieman Jan 9, 2007 at 11:39 AM 13 replies 6.8k views
Original Post
PumpkinPieman
PumpkinPieman
I just got around to working with VBO's a couple days ago and I'm wondering what the performance is like working with streamed data as opposed to vertex arrays. If openGl is assuming that this data will be changed multiple times per frame, wouldn't it be more expensive to bind, lock, fill, bind and unlock then to just keep a pointer to a vertex array and file, bind and buffer (or subsequently bind the array itself without using VBO's)? Another off topic question which I can't find answer to, and I'm not sure if OpenGL or cards support it. But is there a way to bind a matrix data array and a matrix index list array along side verticies?
_the_phantom_
_the_phantom_
My first question would be why are you changing the content of a VBO multiple times per frame?

As for the 2nd question, yes and no.
It won't be a standard array, however you could combine 3 streams of data to send the components required to build an array to a shader and construct it per-vertex.
PumpkinPieman
PumpkinPieman
Well, what I'm doing right now is creating an interface for buffers. I have two DWORD flags, one for static flags, one for stream flags. If VBO's are supported it uses them, otherwise it will just use vertex arrays. I'm just wondering if using VBO's with streams is less efficient then using vertex arrays. Certainly it's easier and probably quicker if you don't need to lock and unlock arrays, I was just wondering if there were any performance gain.

Second:
Do you know of any such implementations so I could get a small example?
_the_phantom_
_the_phantom_
If by streaming you mean doing the following;

- fill buffer
- draw from buffer
- fill buffer
- draw from buffer
- swap backbuffers for next frame

Then yes, it's going to be slow because you are stalling the pipeline often (can't fill a VBO when it's being used as a source). There is a way around it, when updating the VBO you can call the function which does the updating with 'null' or 0 as the source address and then call it again with the real data. The inital call says to the driver 'I'm done with the old data, discard it when you are done and give me a new memory block next time I fill it'.

VBOs really work best however when they are full of static data, or at least data which doesn't change often... this is partly why I'm wondering what kind of usage you'll be giving it to generate this kind problem?

As for the second question, I don't know of any off hand however I'd imagine something like this would work;

varying vec3 row1;varying vec3 row2;varying vec3 row3;void main(){   mat3 m(row1, row2, row3);   // now use 'm' as a normal matrix}


I'm pretty sure that would do it right (I can't recall off the top of my head if matrices fill rowwise or column wise from vectors).

You'd supply the data as you would any other per vertex data.
PumpkinPieman
PumpkinPieman
Well, it will be on a per buffer basis. If the buffer needs to lock FLAG_VERTEX | FLAG_NORMAL, it will do that in one call. Unlock will unlock the flags locked. In terms of a buffer use, yeah. It probably will be drawn that way.
songho
songho
I had an experiment comparing between VBO and VA. The test was updating the positions of all vertices every frame. I found that calculating new positions itself using Vertex Array is much quicker than using the mapped buffer in VBO.

However, the overall performance of VBO is the winner, because actual drawing calls take less time in VBO.

Here is the link of the VBO example: VBO.
There are source codes and a screenshot at the bottom of the page.
PumpkinPieman
PumpkinPieman
Quote:
Original post by songho
I had an experiment comparing between VBO and VA. The test was updating the positions of all vertices every frame. I found that calculating new positions itself using Vertex Array is much quicker than using the mapped buffer in VBO.

However, the overall performance of VBO is the winner, because actual drawing calls take less time in VBO.

Here is the link of the VBO example: VBO.
There are source codes and a screenshot at the bottom of the page.


Thanks! This helped a lot. Here's another question about VBO's. When (or if) the a device is lost, are the contents in static buffers lost as like DirectX?


Other Question:
I've looked around but the main method of doing skeletal animation is primarily done by drawing index sets between matrix calls, and not often using a vertex shader.

Kalidor
Kalidor
Quote:
Original post by PumpkinPieman
Thanks! This helped a lot. Here's another question about VBO's. When (or if) the a device is lost, are the contents in static buffers lost as like DirectX?
There is no such thing as a lost device in OpenGL so you don't need to worry about that. If, for some reason, you destroy your rendering context and create a new one, you will need to recreate all your OpenGL objects (VBOs, textures, etc). SDL used to destroy the rendering context when switching to/from a full-screen window even when it wasn't necessary, but I hear that's fixed now.
Quote:
Original post by PumpkinPieman
Other Question:
I've looked around but the main method of doing skeletal animation is primarily done by drawing index sets between matrix calls, and not often using a vertex shader.
I'm not sure what your question is but I think skeletal animation (especially skinning with multiple vertex weights) is usually done with a static VBO holding the vertices, just changing the bone matrices, and doing the actual skinning in a vertex shader. If not the most common technique, it's certainly the most efficient (unless you're doing several passes).
MARS_999
MARS_999
Quote:
Original post by phantom
Then yes, it's going to be slow because you are stalling the pipeline often (can't fill a VBO when it's being used as a source). There is a way around it, when updating the VBO you can call the function which does the updating with 'null' or 0 as the source address and then call it again with the real data. The inital call says to the driver 'I'm done with the old data, discard it when you are done and give me a new memory block next time I fill it'.




Few things, call glBindBuffer(GL_ARRAY_BUFFER, NULL); means shudown the VBO and allow usage of VA again if you want.

Second, I use VBO's for my particle engine. That is updated every frame to change the vertices positions. I use GL_DYNAMIC_DRAW and I don't see any performance hit with updating the VBO? I am calling glBufferSubData() once per frame though... In fact if anything IIRC my FPS went up with VBOs over VAs...
iNsAn1tY
iNsAn1tY
Sounds like I'm trying to do the same kind of thing you are at the moment. I've put a thin, D3D-style wrapper around OpenGL VBOs, so that client code can change vertex data if required. Basically, you create vertex buffers and index buffers, and use them to create Geometry objects. This way, you can use instances of vertex and index data.

Regarding animation, I have MD5 models animated on the CPU (for the time being, I'm looking into animating them on the GPU). This means that the code which does the animation update requires access to the vertex data in the VBO. Because it isn't changing the size of the data, just the data itself, I'm hoping it will be lightning quick to lock, update, unlock and render the data once a frame. I'm making optimizations like only having one index buffer per model, though, which really cuts down on memory usage.

I guess though that if you're changing the size of your vertex data multiple times a frame, you'll incur quite a performance hit with the GL memory reallocation. And I guess that fragmentation will probably become an issue, too.
My opinion is a recombination and regurgitation of the opinions of those around me. I bring nothing new to the table, and as such, can be safely ignored.[ Useful things - Firefox | GLee | Boost | DevIL ]
PumpkinPieman
PumpkinPieman
I figured they would when using a dynamic buffer, but for something that is streaming and changing the data multiple times I wasn't too sure.

What I'm trying to find out is a good way of implementing's animated meshes. But I haven't been able to find any resourses on implementing this through shaders.
_the_phantom_
_the_phantom_
Quote:
Original post by MARS_999
Quote:
Original post by phantom
Then yes, it's going to be slow because you are stalling the pipeline often (can't fill a VBO when it's being used as a source). There is a way around it, when updating the VBO you can call the function which does the updating with 'null' or 0 as the source address and then call it again with the real data. The inital call says to the driver 'I'm done with the old data, discard it when you are done and give me a new memory block next time I fill it'.




Few things, call glBindBuffer(GL_ARRAY_BUFFER, NULL); means shudown the VBO and allow usage of VA again if you want.


Correct, although I fail to see how that relates to my post as I was on about data update (glBufferData();) not swapping out of VBO mode to VA mode again [smile]

_neutrin0_
_neutrin0_
Yes, calling glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeOfBuffer, NULL, m_usage);
is analogous to calling Lock function in Direct3D with D3DLOCK_DISCARD.

I have implemented an interface abstracting D3D and OGL vertex buffers and I use 2 flags. One for usage set during creation, analogous to usage in Direct3D vertex buffer creation and one for Lock, analogous to D3DLOCK flag used to lock the vertex buffer in Direct3D.

Direct3D discourages the locking of static buffers more than once. Implementing similar functionality in the interface is not very difficult.

As far as vertex arrays go, my personal observation is that excepting very small batches of data (and I mean really small), VBOs always beat VAs. However, in my implementation we fall back to VAs if VBOs are absent. This is for very very old cards. Generally cards today have VBO support.
++ My::Game ++
PumpkinPieman
PumpkinPieman
You guys mind if I get critiques on my implementation of buffers. I normally have no time to myself outside of school, so I've been learning opengl pretty slowly.


// FLAG_VERTEX, FLAG_INDEX, FLAG_TEXTUREUV, FLAG_COLOUR0, FLAG_COLOUR1, FLAG_INDEX
Base IRenderer:// The IRenderer class mainly manages loaded resource lists, and the inner workings of the window and reset systems. All the nitty gritty API specific stuff are done by pure virtual methods. (there is a lot more, I'm just listing the buffer ones)// DATAPTR can only be changed by IRenderer, all of DATAPTRs variables are private. So it acts much like an ID system with an attached pointer.// If flags below are both static and stream flags they are dynamic flagsCreateBuffer(DATAPTR& dp, DWORD nVertex, DWORD nIndex, DWORD staticFlags, DWORD streamFlags);// bd contains a pointer to each of the locked flags (Vertex*, Normal*, Colour0*, etc ..) and a Vert and Index count.LockBuffer(const DATAPTR& dp, BUFDATA& bd, DWORD lockFlags);UnlockBuffer(const DATAPTR& dp);KillBuffer(DATAPTR& dp);// RENDERERBUFFER contains flags, vertex counts, index counts, and a BUFDATA structure for all unlocked streams. Mainly all the information the API needs to create a buffer. As well as a void* to whatever extra data the API needs. The IRenderer methods check the DATAPTR first if the proper resource is passed.virtual pure CreateBuffer(RENDERERBUFFER& buffer)virtual pure LockBuffer(RENDERERBUFFER& buffer, DWORD unlockFlags)virtual pure UnlockBuffer(RENDERERBUFFER& buffer)virtual pure ResetBuffer(RENDERERBUFFER& buffer)virtual pure KillBuffer(RENDERERBUFFER& buffer)


What I plan on doing is have methods to bind specific streams to OpenGL
BindBuffer(DATAPTR& dp, DWORD flags)

So this method _could_ be used to bind two buffers but different data streams. This may not be entirely possible in DirectX however.


Inside IRendererOGL, the buffer system works much like what was said above. If VBO's don't work with the system, VAs are used in place.
Figgles
Figgles
iNsAn1tY, I think you should look into GL_ARB_vertex_blend and GL_ARB_matrix_palette to do hardware skeletal animation without vertex programs (shaders, whatever). I used to do software skeletal animation by first keeping a local copy of the mesh in memory, then mapping a vertex buffer, and then used some SSE (I love amd64's 16 xmm registers) to do the transformations, writing the results into the memory directly. This worked fairly well actually, but the GPU can still do it so much faster. Yay FF pipeline ;)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.