Question

I have a bit of OpenGL 2.1 rendering code that works great when using an nVidia card/driver or the open-source AMD driver, but doesn't work when using the official fglrx driver. It just displays a grey screen (the glClear colour) and doesn't draw anything.

gDEBugger shows that glDrawElements is giving the error GL_INVALID_OPERATION. According to this page (What can cause glDrawArrays to generate a GL_INVALID_OPERATION error?) there are a lot of half-documented possible causes of this error. The shader is compiling fine, and the buffer size should be good too, and I am not using geometry shaders (obviously). It's just a simple draw call for a cube, with only one vertex attribute. Code is below.

glUseProgram(r->program->getProgram());

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->texture->glID );
glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0);
glUniform4f(r->program->getUniform("colour").location, r->colour.x, r->colour.y, r->colour.z, r->colour.w);

glBindBuffer(GL_ARRAY_BUFFER, r->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, r->ibo);

glVertexAttribPointer(
    r->program->getAttribute("position").location,  // attribute
    3,                                  // size
    GL_FLOAT,                           // type
    GL_FALSE,                           // normalized?
    sizeof(GLfloat)*3,                  // stride
    reinterpret_cast<void*>(0)          // array buffer offset
);
glEnableVertexAttribArray(r->program->getAttribute("position").location);

glUniformMatrix4fv(r->program->getUniform("modelToCameraMatrix").location, 1, GL_FALSE, glm::value_ptr(modelToCameraMatrix));

glDrawElements(
    r->mesh->mode,                  // mode
    r->mesh->nrOfInds,              // count
    GL_UNSIGNED_SHORT,              // type
    reinterpret_cast<void*>(0)      // element array buffer offset
);

I have no idea what is going on or what might be causing this error. If anyone has any pointers as to what might be causing this to happen with the fglrx driver and not with any other driver, I would be glad to hear it. If you need more code, I'll happily provide it of course.

Was it helpful?

Solution

Let's dissect this:

glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);

The OpenGL 2.1 Spec states in section 2.15:

Setting a sampler’s value to i selects texture image unit number i. The values of i range from zero to the implementation-dependent maximum supported number of texture image units.

Setting the value of a sampler is only legal with calls to glUniform1i{v}.

If an implementation takes the liberty to allow statements like the above, it has to do some subtraction under the covers - i.e. it has to map (GL_TEXTURE0 + i) to some value in [0, MAX_UNITS - 1]. (Note: MAX_UNITS is just symbolic here, it's not an actual GL constant!).

This is simply non-conformant behavior, since GL_TEXTURE0 defines a very large value and which is far out of range of any ancient or modern GPU's number of available texture units.

BTW, GL_TEXTURE0 is not a prefix, it's a constant.


The GL 4.4 core specification is much more clear about this in section 7.10:

An INVALID_VALUE error is generated if Uniform1i{v} is used to set a sampler to a value less than zero or greater than or equal to the value of MAX_COMBINED_TEXTURE_IMAGE_UNITS.

Regarding glActiveTexture(): This functions takes a value in [GL_TEXTURE0, GL_TEXTURE0 + MAX_UNITS - 1]. It probably doesn't have to be like this, but that's the way it is defined.

It's no secret among the OpenGL community, that NVIDIA is often more lenient and their drivers sometimes take stuff that simply doesn't work on other hardware and drivers because it's simply not supposed to work.

As a general rule: never rely on implementation-specific behaviour. Rely only on the specification. If conformant and correct application code does not work with a specific implementation, it's a bug in the implementation.

OTHER TIPS

I think I have found out what is wrong with the above code. The faulty line is

glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);

which should be

glUniform1i(r->program->getUniform("texture").location, i);

I'm not really able to find out exactly why it should be like this (glActiveTexture does need the GL_TEXTURE0 prefix), and why only the AMD driver craps out over it, so if anyone can elaborate on that, I would love to hear it. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top