8
\$\begingroup\$

I was learning OpenGL recently. In games, we need to update the position of game objects frequently, and they will come in & out of screen constantly. So it means in rendering we need to update the vertex buffer quite often as well.

In OpenGL's context, one intuitive way is to use glBufferSubData to update those that changed.

But I also read online a trick called Orphaning that creates a new buffer data and upload the whole vertices data to it.

Also due to state chagnes cost and uploading cost, multiple glBufferSubData may also cost more.

Here is my question,

  1. Which method is better?
  2. Is stalls really matters in this case?
  3. Is state changes and uploading cost really matters in this case?

Thanks!

\$\endgroup\$
  • \$\begingroup\$ Why do you want to re-upload geometry when you can actually only update the matrix? in other words you don't need to re-upload vertices when only transforms need to change. \$\endgroup\$ – concept3d Nov 10 '14 at 8:07
  • \$\begingroup\$ @concept3d Yeah, you're correct! I made a mistake when writing the problem descriptions. Thank you for reminding me of that! I updated the descriptions already. \$\endgroup\$ – YiFeng Nov 10 '14 at 8:16
9
\$\begingroup\$

This is a complex question with a lot of small details that really matter, the performance will vary based on platform and application. So you should profile for possible bottlenecks before investing in optimizations.

That said, firstly, I assume you should reduce uploads and updates as much as you can, for example use instancing.

Secondly, note that GPUs can’t transfer buffers and render at the same time, so all OpenGL commands in the command queue are processed sequentially by the device. There are different ways to copy data and/or make it avaialbe to be used by the GPU.

There are various ways to stream data to GPU

1-glBufferData or glBufferSubData method

Using glBufferData or glBufferSubData is like memcpy. you pass a pointer and DMA operation might be performed, I said might because memory might be pinned in CPU memory and used directly by the GPU without actually a memory transfer to GPU happening, depending on the usage flag (GL_STREAM). In opinion you should try this at first because it's simpler to implement.

2- getting a pointer to internal memory using glMapBuffer

If the above isn't good enough you can use glMapBuffer, you get a pointer to internal memory, and you can use this pointer to full the buffer directly, this is good with file read and write operations, as you can directly map the file data to GPU memory rather than copying to a temp buffer first. If you don't want to map the whole buffer you can use glMapBufferRange which can be used to map a portion of the buffer.

One trick is to create a large buffer, use the first half for rendering and the second half for updating.

3- Buffer Orphaning

Regarding buffer orphaning this can be done using glBufferData with null and the same parameters it had. The driver will return the memory block once it's not used. And will be used by the next glBufferData call (no new memory will be allocated).

All the methods mentioned cause alot of expensive sync, again GPUs can’t transfer buffers and render at the same time.

4- Unsynchronized Buffers

The fastest (and hardest to get right) method is to use buffers without synchronization you can use GL_MAP_UNSYNCHRONIZED_BIT flag with glMapBufferRange, the problem is that no sync is done, so we might upload data to a buffer begin used, and hence screwing everything up. You can use multiple buffers with unsync bit to make things a little bit easier.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

I do it another way. May be something is wrong there. Some hidden dangerous things.

(I use C# + OpenTK.GameWindow)

For instancing of the object I use a separated Array Buffer Object for the set of model matrices for every instance. In the vertex shared:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

In my C# code matrices are stored in a float array float[] mat4_array

Then I bind the array to Array Buffer Object:

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

Every frame the model matrices are updated. To update mat4_array I just call:

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

and render the scene. using GL.DrawElementsInstanced.

There is no additional OpenGL calls and it works perfectly.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ What is the purpose of having the model matrix as vertex attribute? \$\endgroup\$ – Anton Duzenko Sep 7 '17 at 8:40
  • \$\begingroup\$ It's for the instance object draw. And it's faster than an array passed as an uniform variable. \$\endgroup\$ – Dennis Sep 8 '17 at 9:27

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.