Mapping a VBO more than once per frame

In my VBO code, I generate 4 ARRAY_BUFFER VBOs of 256Kb each, which I use to stream dynamic vertex data into. I set these up with GL_STREAM_DRAW_ARB usage.

I don’t know what the new vertex data is in advance, so I can’t use BufferSubData, but instead use MapBuffer.

Each VBO has a wrapper over it indicating how much of it has been written to, so when MapBuffer is called, I offset the returned pointer to where I am allowed to write, and update that offset when UnmapBuffer is called. This MapBuffer/UnmapBuffer pairing, with writes, could happen multiple times in a frame, on the same VBO.

However, I’ve noticed that using this approach causes a varying amount of corruption in the rendered geometry.

Under the VBO specs, it mentions that a PRESERVE/DISCARD option isn’t really necessary.

So I’m wondering whether whenever you call MapBuffer, is any of the data already in the buffer preserved? or discarded?

I’m leaning towards thinking it’s the latter (for speed), which would explain the corruption I’m seeing.

Has anyone else looked at anything like this? Or is it assumed that you’d only map any VBO at most once in any frame?

Cheers,

Mark

Are you checking the return of glUnmapBufferARB()? It returns false if the data is lost, in which case you are required to respecify it.

– Tom

Yes. It never returns GL_FALSE. If it does, I send out a debug message and then respecify the VBO with a NULL pointer data to glBufferDataARB, but I’ve never seen that happen.

I should say that I’m seeing the same thing on both a GeForce and a Radeon card, so I’m pretty sure it’s my use of VBOs that is in the wrong.

Does anyone else use shared VBOs for streaming like me, or do each of their dynamic geometries have a streaming VBO (maybe 2 for double buffering)?

(I won’t be able to post an example either as it’s company code.)

Mark

OK, so I just did the obvious thing and looked at the memory at the pointer returned by MapBuffer under a debugger. This may be an unfair test as it’s not running in realtime anymore, but heck.

Anyway, the vertex data written to the VBO seems to be preserved across calls to MapBuffer in the same frame. This was on a GeForce4 Ti4600 with latest drivers.

I guess it could be a buffering issue now…

Mark

OK, so ignore this post now… it did turn out to be my use of VBOs.

My test data that was failing contained static color and texture coordinates, as well as dynamic positions and normals, but I was updating which VBO the static stream was using rather than the dynamic stream!

Do’h.

Just to clarify, you should not expect to see data preserved when you map a VBO. This is a mechanism that allows the driver to rename the buffer. If any data is preserved, it is merely coincidence. Don’t expect it to always be that way.

Thanks -
Cass

Cass,

So the approach I’ve taken, where I can possibly map the same VBO more than once per frame, and expect the data to be there, is inherently flawed?

Mark

IMHO, by my tests, for best perfomance to render dynamic data(for example Skinned Mesh) you need:
Init (one time at start application):
GenBuffer(1,&ID);

Render (each render cycle):
for each objects {
BindBuffer(…,ID);
BufferData(…,size_of_data_for_object, NULL, …); // Alloc new memory, it’s fast when pMem == NULL
MapBuffer(…);
FillBuffer(…);
UnmapBuffer(…);
RenderObject(…);
};

glBufferDataARB allow fast MapBuffer operation, because it’s don’t wait for sync from render previeus object and allow async write to video memory and render.
ARB memory manager will delete buffer of previeus object when it finish rendering.

Some time ago i wrote same manager for VAR extensions with fence sync for mem blocks, it work very well, and i see same perfomance for VBO. I think it’s same algorithms.

That’s a very interesting trick. But are you sure there’s really a performance improvement when you do a “NULL” BufferData call ? Did you make a comparison ? If so, what were the results ?

Y.

Is this implementation specific? It is always a speed-up, or just for the particular card/driver combo you’re using?

I just did a very quick test, inserting a BufferDataARB command with a NULL data pointer before my MapBuffer, as suggested by CybeRUS.

It’s not a heavy-weight example, just morphing geometry (so may not give accurate results), but I didn’t see any performance benefits, and sometimes the performance seemed slightly lower. (I’m only measuring FPS, but seems not unreasonable since an extra command is in there.)

I did this on both a GeForce4 Ti4600 and a Radeon 9700 Pro (both with latest drivers).

It may be good for some approaches, but I can’t see it giving me anything extra as far as I can see.

Mark

I’m using GeForce 3, and detanator 43.25 (WHQL)
I have some perfomance improvement, but it’s small.
The scene consists of 10 human models, about 4K tri on each model, with skinning on CPU. Perfomance change about 3%-5%. And it’s enought for me.
I found something in specification of VBO:
[i]
Should there be a PRESERVE/DISCARD option on BufferSubDataARB? On
MapBufferARB?

    RESOLVED: NO, NO.  ATI_vertex_array_object had this option for
    UpdateObjectBufferATI, which is the equivalent of
    BufferSubDataARB, but it's unclear whether this has any utility.
    There might be some utility for MapBufferARB, but forcing the
    user to call BufferDataARB again with a NULL data pointer has
    some advantages of its own, such as forcing the user to respecify
    the size.

[/i]

I think when i have new size, then i have new buffer. May be i’m wrong.

From the spec:
[i]
Should there be a new command “DiscardAndMapBuffer” that is
equivalent to BufferDataARB with NULL pointer followed by
MapBufferARB?

    RESOLVED: NO, no one has come up with a clearly superior proposal
    that everyone can agree on.

[/i]

Ugh. You’re right. I was thinking of an older version of the spec, where MapBuffer behaved like BufferData with a null data pointer.

Note that for streaming data, you may well
want to do
BufferData()
MapBuffer()

UnmapBuffer()
draw()

using the same buffer over and over. This allows the driver to easily rename the one-use buffer.

Thanks -
Cass

OK, so just to confirm, can we expect any data preservation when you call MapBuffer or not?