Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL ES 2.0: The most efficient setup for a VBO with GL_STREAM_DRAW?

I'm using a Vertex Buffer Object (VBO) in OpenGL ES 2.0.

I have a set of vertex data which is permanently stored in normal RAM. The reason is that calculating the vertex positions from scratch is costly, but a delta can be added to the last position to cheaply update it.

The actual number of vertices to be drawn changes rapidly over time. In one frame I may have 1000 and in the next 2500. Following advice received here earlier, I now specify integer UPPER as an upper bound on the number of vertices which will ever be drawn. I malloc my vertex and index data arrays only once at startup based on this value.

I pass the GL_STREAM_DRAW usage hint to each glBindBuffer call to indicate the data changes each frame.

Attempting to be as efficient as possible, I created the following setup:

// SETUP: Called only once.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for UPPER vertices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (UPPER-1).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.

// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferSubData(GL_ARRAY_BUFFER,...); // Update VBO data.

// RENDER: Called on each frame.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...); // Number of vertices and indices to be used is inherently specified in here.

However this breaks with an EXC_BAD_ACCESS on glDrawElements, and I know it's due to my ordering of gl commands.

I had a similar setup earlier which worked:

// UPDATE: Called when vertex data changes (on each frame).
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,...); // Pass index values 0 - (actual number of vertices to draw - 1)

// RENDER: Called on each frame.
glBindBuffer(GL_ARRAY_BUFFER,...);
glBufferData(GL_ARRAY_BUFFER,...); // Pass vertex data for actual number of vertices (not UPPER).
glEnableVertexAttribArray(...); // Setup vertex attributes.
glVertexAttribPointer(...);
glUseProgram(...); // Use program with custom shaders.
glUniformMatrix4fv(...); // Identify shader uniforms.
glDrawElements(GL_TRIANGLES, numberOfVerticesThisFrame, ...);

However, this setup requires much more work per frame, and as you can see involves changing the VBO sizes (since it uses actual size, not UPPER), which I've been told is a big performance drain.

Could someone please explain to me any obvious problems with my new setup, and most importantly which commands I have to call every frame before glDrawElements? My assumption that I can prepare all possible indices in advance and then pass the actual vertex count to glDrawElements is clearly wrong.

like image 731
KomodoDave Avatar asked Sep 05 '11 00:09

KomodoDave


1 Answers

To answer the question you asked in your question title, there is no "most efficient" buffer object setup for streaming vertex data. Especially not on ES 2.0, which covers a wide range of different hardware, each with its own peculiarities.

To answer your question about why your code stopped working, it's likely because you're not respecting what these functions actually do.

For example, glUseProgram causes the given program object to become the program object that any subsequent glDraw* calls will use until you call glUseProgram again. Think of most OpenGL functions as poking at global state, because that's how it works. glUseProgram is setting a global variable that glDraw* reads to find out what shader to use.

Therefore, if you want to ensure that a particular draw call uses a particular shader, then you must glUseProgram that shader immediately beforehand. Or at least recently enough that you know that you didn't change it somewhere else. Generally, rendering for an object looks like this:

  • Establish the vertex attributes for rendering the object.
  • Set the current program, and change any per-object uniforms (matrices, etc).
  • Bind textures for the program, if any.
  • Bind other state for the program, if any.
  • Render.
  • Deactivate attributes

The first step uses glEnableVertexAttribArray, glBindBuffer, and glVertexAttribPointer. These functions, just like glUseProgram set global state. You should use glDisableVertexAttribArray after having rendered with the object, and you should unbind any buffers you may have used.

like image 52
Nicol Bolas Avatar answered Nov 15 '22 06:11

Nicol Bolas