Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use Index Buffer Objects (IBO) with the function 'glMultiDrawElements'?

Tags:

opengl

glsl

I develop a small 3D engine using OpenGL and GLSL.

I have incorporated a vertex data batching system which the goal is to gather all the geometry (all the objects) sharing the same shader program and same transformations in a unique Vertex Buffer Object (VBO) thereby minimizing the state changes (binding) and draw calls too.

I currently use the function 'glMultiDrawElements' to render a specific batch of data (so a single draw call). So, for instance, if I have in my batch 3 meshes, I have 3 arrays of indices too (one for each mesh) organized in a 'GLvoid **' array (a double array). So to render my 3 meshes I have a unique call of glMultiDrawElements but I have to pass directly to the function the double array of elements in parameter.

But I wonder (for a question of performance) if it's possible to store all the elements (the double array of elements) in a Index Buffer Object (IBO)(like it's possible to do with glDrawElements -> here a simple array of elements) and bind it before the glMultiDrawElements call... I don't think so because it's a double array and not a simple array but maybe (and I hope so) I'm wrong.

Here's an example using glDrawElements (pseudo code):

[...]

//Setup data

[...]

#define OFFSET_BUFFER(offset) ((char*)NULL + offset)

foreach (pMeshGeometry from meshes) //iterates for each mesh of the scene
{
    pMeshGeometry->GetIndexBuffer().Lock(); //Bind IBO
    {
         glDrawElements(pMeshGeometry->GetPrimitiveType(),       
             pMeshGeometry->GetIndexBufferSize(), pMeshGeometry->GetIndexBuffer().GetType(), OFFSET_BUFFER(0)); //Render a specific mesh eccording to indices
    }
}

For the moment I use glMultiDrawElement this way:

glMultiDrawElements(GL_TRIANGLES, &this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    (const GLvoid **)&this->m_IndexAttribArray[0], this->m_CountElementArray.size()); //I enter the array of pointer directly in parameter

So, maybe to following example should be possible:

#define OFFSET_BUFFER(offset) ((char**)NULL + offset) //Something like this

glMultiDrawElements(GL_TRIANGLES, &this->m_CountElementArray[0], GL_UNSIGNED_INT,
                    OFFSET_BUFFER(0), this->m_CountElementArray.size()); //Something like this

So if it's not possible to do this, I've thought to the function 'glDrawRangeElements'. For my example of 3 meshes into a unique VBO, I'll just have to bind an IBO before each glDrawRangeElements call (here, 3 draw calls for each mesh -> so a loop of glDrawRangeElements). So here it's clearly possible to use IBO.

This method will work for sure but I don't think it's the best one! I think it's possible to do this using glMultiDrawElements with an IBO but I don't know how to do this.

Or maybe it's really impossible. Maybe the fact to enter directy in parameter an array of indices is faster than the method using glDrawRangeElements and its IBO and so the usage of IBO in this case is maybe deprecated and so not adapted.

What do you think of that ?

like image 740
user1364743 Avatar asked Jul 01 '14 18:07

user1364743


People also ask

What are index buffers OpenGL?

Buffer Objects are OpenGL Objects that store an array of unformatted memory allocated by the OpenGL context (AKA the GPU). These can be used to store vertex data, pixel data retrieved from images or the framebuffer, and a variety of other things.

What is an index buffer?

An index buffer is essentially an array of pointers into the vertex buffer. It allows you to reorder the vertex data, and reuse existing data for multiple vertices.


1 Answers

You certainly can use index buffers with glMultiDrawElements(). Client side index arrays are deprecated in the OpenGL Core Profile. So if glMultiDrawElements() could not operate with index buffers, there would be no way to use it anymore.

To see how it works, we need to look at what the arguments of glMultiDrawElements() mean. The call is basically just a shortcut for multiple glDrawElements() calls. The signature is:

void glMultiDrawElements(GLenum mode, const GLsizei* count, GLenum type,  
                         const GLvoid** indices, GLsizei primcount); 

Aside from some error checking details, this call is equivalent to:

for (int i = 0; i < primcount; ++i) {
    glDrawElements(mode, count[i], type, indices[i]);
}

Now remember that if an index buffer is bound, the last argument to glDrawElements() is a relative offset into the buffer. So the corresponding 4th element of glMultiDrawElements() is an array of offsets into the buffer. The 2nd argument is a matching array of counts.

People often use a macro to hide the cumbersome type conversion of the last argument of glDrawElements(). Using this:

#define BUFFER_OFFSET(offset) (static_cast<char*>(0) + (offset))

As an example, let's say we have an index buffer bound, and we want to draw 3 sub-ranges of the index array with a single call:

  • 20 indices starting at index 10 in the buffer.
  • 30 indices starting at index 40 in the buffer.
  • 10 indices starting at index 90 in the buffer.

I'll use unsigned shorts (GLushort) for the index type. So the index buffer would have been populated with data from a GLushort indexA[100] at some point in the past. The setup and draw call then looks like this:

GLsizei count[3] = {20, 30, 10};
GLvoid* indices[3] = {
    BUFFER_OFFSET(10 * sizeof(GLushort)),
    BUFFER_OFFSET(40 * sizeof(GLushort)),
    BUFFER_OFFSET(90 * sizeof(GLushort)),
};
glMultiDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_SHORT, indices, 3);
like image 151
Reto Koradi Avatar answered Nov 15 '22 11:11

Reto Koradi