How to fully unbind a GL_ELEMENT_ARRAY_BUFFER?

2019-08-01 04:14发布

问题:

I'm working on a multithreaded app that simultaneously renders geometry to multiple FBOs. I'm experiencing a leak (as described on this question).

I've been able to narrow it down a little — if I make one change, it stops leaking — but I can't figure out why.

On each of 4 threads (each with its own shared context), I'm doing the following each render cycle:

// Upload

positionBuffer = getUnusedArrayBufferFromPool();
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*vertexCount, positions, GL_STREAM_DRAW);

{
    GLuint vertexArray;
    glGenVertexArrays(1, &vertexArray);
    glBindVertexArray(vertexArray);

    elementBuffer = getUnusedElementArrayBufferFromPool();
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*elementCount, elements, GL_STREAM_DRAW);

    glBindVertexArray(0);
    glDeleteVertexArrays(1, &vertexArray);
}

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);



// Render (possibly on a different context)

GLuint vertexArray;
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);

glUseProgram(programName);
{
    GLint positionAttribute = glGetAttribLocation(programName, "position");
    glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
    glVertexAttribPointer((GLuint)positionAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)0);
    glEnableVertexAttribArray((GLuint)positionAttribute);

    {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
        glDrawElements(GL_TRIANGLES, (GLsizei)elementCount, GL_UNSIGNED_INT, (void*)0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    glDisableVertexAttribArray((GLuint)positionAttribute);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glUseProgram(0);

glBindVertexArray(0);
glDeleteVertexArrays(1, &vertexArray);



// Cleanup (possibly on a different context)

glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
throwArrayBufferBackInPool(positionBuffer);

//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, 0, GL_STREAM_DRAW);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//throwElementArrayBufferBackInPool(elementBuffer); // Why does this leak if we recycle it?
glDeleteBuffers(1, &elementBuffer);

If I swap the last 2 lines — if I throw the element buffer back into the pool instead of deleting it each render cycle — OpenGL Driver Monitor indicates a huge leak.

But I'd prefer to pool it, since calling glDeleteBuffers() each frame is really slow.

What am I missing? I assume I'm failing to unbind elementBuffer from something — and that something is holding on to a reference to it, causing the leak — but I can't figure out what.


Edit: Just tested on a different system (Mac OS 10.6) — on that system, it leaks if I recycle any buffers.

Edit: I modified my application so that GL_ARRAY_BUFFER is pooled separately from GL_ELEMENT_ARRAY_BUFFER, and so that elementBuffer is consistently bound to GL_ELEMENT_ARRAY_BUFFER. But it still leaks if I throwElementArrayBufferBackInPool(elementBuffer);.

Edit: Clarified why I'm creating and deleting a VAO during Upload and during Rendering — because they might happen on different shared GL contexts, and VAOs can't be shared between contexts.

Edit: I modified my application to provide zero-size buffer data before throwing the buffers back into the pool, but it still leaks just as quickly.

回答1:

Your buffer pool is used for binding GL_ARRAY_BUFFER as well as GL_ELEMENT_ARRAY_BUFFER objects. Worse, you're first using elementBuffer for binding a GL_ARRAY_BUFFER, but then carry on using it for an GL_ELEMENT_ARRAY_BUFFER. I haven't fully wrapped my head around it, but somewhere between mixing the namespaces and the inconsistent use of bindings I'd put my money on that.

My suggestion: Create a separate pool of buffer names for GL_ELEMENT_ARRAY_BUFFER use and make sure you're using it consistently for only that.