Search code examples
c++opengl-esrenderingvertex-buffer

OpenGL Batching: Why does my draw call exceed array buffer bounds?


I trying to implement some relatively simple 2D sprite batching in OpenGL ES 2.0 using vertex buffer objects. However, my geometry is not drawing correctly and some error I can't seem to locate is causing the GL ES analyzer in Instruments to report:

Draw Call Exceeded Array Buffer Bounds

A draw call accessed a vertex outside the range of an array buffer in use. This is a serious error, and may result in a crash.

I've tested my drawing with the same vertex layout by drawing single quads at a time instead of batching and it draws as expected.

// This technique doesn't necessarily result in correct layering, 
// but for this game it is unlikely that the same texture will 
// need to be drawn both in front of and behind other images.
while (!renderQueue.empty()) 
{
    vector<GLfloat> batchVertices;
    GLuint texture = renderQueue.front()->textureName;

    // find all the draw descriptors with the same texture as the first 
    // item in the vector and batch them together, back to front
    for (int i = 0; i < renderQueue.size(); i++) 
    {
        if (renderQueue[i]->textureName == texture)
        {
            for (int vertIndex = 0; vertIndex < 24; vertIndex++) 
            {
                batchVertices.push_back(renderQueue[i]->vertexData[vertIndex]);
            }

            // Remove the item as it has been added to the batch to be drawn 
            renderQueue.erase(renderQueue.begin() + i);
            i--;
        }
    }

    int elements = batchVertices.size();
    GLfloat *batchVertArray = new GLfloat[elements];
    memcpy(batchVertArray, &batchVertices[0], elements * sizeof(GLfloat));

    // Draw the batch
    bindTexture(texture);
    glBufferData(GL_ARRAY_BUFFER, elements, batchVertArray, GL_STREAM_DRAW);
    prepareToDraw();
    glDrawArrays(GL_TRIANGLES, 0, elements / BufferStride);

    delete [] batchVertArray;
}

Other info of plausible relevance: renderQueue is a vector of DrawDescriptors. BufferStride is 4, as my vertex buffer format is interleaved position2, texcoord2: X,Y,U,V...

Thank you.


Solution

  • glBufferData expects its second argument to be the size of the data in bytes. The correct way to copy your vertex data to the GPU would therefore be:

    glBufferData(GL_ARRAY_BUFFER, elements * sizeof(GLfloat), batchVertArray, GL_STREAM_DRAW);
    

    Also make sure that the correct vertex buffer is bound when calling glBufferData.

    On a performance note, allocating a temporary array is absolutely unnecessary here. Just use the vector directly:

    glBufferData(GL_ARRAY_BUFFER, batchVertices.size() * sizeof(GLfloat), &batchVertices[0], GL_STREAM_DRAW);