Jump to content
  • Advertisement
Sign in to follow this  
noodleBowl

Quickest way to glBufferSubData

This topic is 991 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was wondering what is the best way to put data to a buffer using glBufferSubData?
I'm talking in the sense that since I can't map directly into the buffer what should I do?

Do I make a Vector and then just use the call?
std::vector<GLfloat> vec;

//Code that puts  data into the vector

glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * DataSize, &vec[0]);

Share this post


Link to post
Share on other sites
Advertisement

This is, unfortunately, one of those things you're going to have to benchmark yourself, with the understanding that you may get different performance using different methods on different hardware, and overall performance is going to be dependent on factors local to your program such as: is the buffer currently in use for drawing, what your memory pressure is like, how big the buffer is and how much data you're writing to it (which need not be the same thing), phases of the moon, cosmic rays, etc.

 

I'm only being half-facetious.  The reality is that OpenGL (and GL ES) makes no guarantees whatsoever about performance.  It's a functional specification that on occasion may include words such as "may help some implementations perform better", but as a general rule, performance is not specified: it's implementation-dependent.

 

That said, glBufferSubData should always be reasonably well-behaved but it may need to copy the data to temporary driver-allocated memory if it can't update the underlying buffer object immediately.  That incurs extra overhead from the memory copy, but sometimes that's a better tradeoff than blocking so that the update can occur.  If the buffer size doesn't change then fully respecifying the buffer with glBufferData may be better: the driver might automagically double-buffer behind the scenes for you.  Otherwise you could attempt your own double buffering.

 

But like I said at the start, there is no "best way" that can be recommended and that will be suitable for all programs on all hardware.  You need to experiment yourself with a number of different ways and adapt your update strategy to your own local conditions.

Share this post


Link to post
Share on other sites
You state that you cannot "map the buffer" but did not explain why. You should be doing that. glBufferSubData is one of those confusing anachronisms that OpenGL is filled with.

http://gamedev.stackexchange.com/questions/87074/for-vertex-buffer-steaming-multiple-glbuffersubdata-vs-orphaning
http://stackoverflow.com/questions/12244422/updating-vertex-data-in-a-vbo-glbuffersubdata-vs-glmapbuffer

Note that with code like `&vec[0]` you need to be sure to only call that if the vector is not empty. You are far better off using vec.data() instead.

Share this post


Link to post
Share on other sites

I don't really understand the question, the only way to put data in a buffer using glBufferSubData, is using glBufferSubData. If you are asking whats the best way to update a buffer, then that is a different question( ..and forgive me, since I may be making an assumption that English is your native tongue ), but its hard to give a meaningful answer without the correct question. If you can't map the buffer, then glBufferSubData is your next best option.

Share this post


Link to post
Share on other sites

You state that you cannot "map the buffer" but did not explain why

 
I can't map into the buffer directly cause there is no glMapBuffer functions for OpenGL ES 2.0. Only ones I have available are glBufferSubData or glBufferData
 

I don't really understand the question, the only way to put data in a buffer using glBufferSubData, is using glBufferSubData. If you are asking whats the best way to update a buffer, then that is a different question( ..and forgive me, since I may be making an assumption that English is your native tongue ), but its hard to give a meaningful answer without the correct question. If you can't map the buffer, then glBufferSubData is your next best option.

 
I'm just wondering how I should be using glBufferSubData. Since glBufferSubData does not return a pointer to the buffer directly like glMapBuffer. glBufferSubData requires, to my knowledge, an intermediate spot like an array or vector to retrieve the data from. Basically:
 
1. Copy data to "intermediate area" such as a vector
2. glBufferSubData call using the "intermediate area" as the data param
 
I was hoping I could avoid the "intermediate area" copy, but I guess not

 

http://gamedev.stackexchange.com/questions/87074/for-vertex-buffer-steaming-multiple-glbuffersubdata-vs-orphaning

 
Speaking about the orphaning part. Can someone explain this to me?
From what I understand, lets say I have a buffer that can hold 1000 sprites. Then I fill this buffer with 1000 sprites. So the buffer is now full. Then I want another sprite, now this is where I orphan. I make a call out to glBufferData() using NULL as my data param. And I get a fresh block of memory
 
Now I can write to this block of mem and I still have all the previous data (the first 1000 sprites) sitting out on the GPU
The only caveat is that any time I orphan I have to reallocate the buffer space.  Right?

Share this post


Link to post
Share on other sites

You may want to look into whether OES_mapbuffer is available on your target platforms.

 

 


From what I understand, lets say I have a buffer that can hold 1000 sprites. Then I fill this buffer with 1000 sprites. So the buffer is now full. Then I want another sprite, now this is where I orphan. I make a call out to glBufferData() using NULL as my data param. And I get a fresh block of memory
 
Now I can write to this block of mem and I still have all the previous data (the first 1000 sprites) sitting out on the GPU
The only caveat is that any time I orphan I have to reallocate the buffer space.  Right?

When you call it with a NULL data pointer (and the same size as before), you do not "get a fresh block of memory". It merely tells the driver that you no longer care about what was in the memory. It may be the same memory, it may be new memory, it may be a mix of things. Your sprites may or may not be preserved, depending on what the system's doing at that moment in time. All draw calls prior to that point will not be affected, but there are no promises after that. It is legal to ignore the call entirely. It's an optimization technique to try and avoid stalls when uploading data, not a rule about how things behave.

 

In general, one of two things will happen. Either the driver will need the contents of that buffer for a submitted draw call that has not yet been sent through the pipeline, in which case it will allocate a new block of memory. This case is going to perform slowly. Or the driver is done with the memory, and it will simply do nothing. Doing nothing is pretty fast. Long story short, doing this more than about once per frame on any given buffer is more or less equivalent to simply manually creating new buffers and tends to show poor performance. The bad news is that all of the sane mechanisms for handling buffers in OpenGL did not make it into ES 2.0 and exist only as extensions. MapBuffer is good, MapBufferRange is better. If you cannot use MapBufferRange, it's best to simply allocate lots of buffers ahead of time and avoid uploading data to them more than once per frame. 

 

Also consider that ES devices are typically running unified memory and that simply omitting VBOs outright and submitting draw calls from client memory may be faster than doing any of this. Maybe.

 

Lastly, I recommend reading this book chapter.

Edited by Promit

Share this post


Link to post
Share on other sites

To expand further on Promit's response regarding orphaning, the idea is that after a few frames the driver reaches a steady state where it's no longer allocating new blocks of memory but instead reusing peviously allocated blocks that are no longer in use for drawing.

 

How this works (in theory because none of the specifications make any promises about it) is that when the GPU is finished with a buffer that had been orphaned, the driver need not free the memory immediately.  It may instead decide to keep the memory allocated for a few extra frames in case you orphan again; in that case the driver does not need to do a new allocation at all - it just reuses the memory from a few frames ago and the net result is just as fast as the "doing nothing" case.

 

So long as you orphan on a predictable and repeatable schedule, the driver should be able to detect the pattern and - aside from the first few frames when new allocations will occur - you'll never have new allocations.  Instead the driver will just be continually reusing previously allocated memory that it chose to not free for this explicit purpose.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!