OpenGL error C5208: Sampler needs to be a uniform


#1

One of my users is having trouble running my application as he is facing this issue on startup:

image

These are his specifications:

OpenGL Version: 3.3.0
ShadingLanguageVersion: 3.30 NVIDIA via Cg compiler
OpenGL Renderer: Quadro FX 1700/PCIe/SSE2
OpenGL Vendor: NVIDIA Corporation

The problem fragment shader is:

layout (location = 0) out vec4 gColor;
layout (location = 1) out vec4 gNormal;

in vec2 UV;
flat in vec4 normal;
flat in float texID;
in vec3 position;

uniform sampler2D texColour;

uniform sampler2D colourTexture[16];
uniform sampler2D normalTexture[16];

void main()
{
    if (texID == 0)
    {
        vec4 c = texture(texColour, UV);
        gColor = vec4(c.rgb * normal.w, c.a);
        gNormal = vec4(normal.xyz, 1.0);
    }
    else
    {
        vec4 n = texture(normalTexture[int(texID)], UV);
        // This line has the issue:
        gColor = vec4(texture(colourTexture[int(texID)], UV).rgb * normal.w, 0.0);
        gNormal = vec4(normalize(n.xyz * 2.0 - 1.0), n.w);
    }
}

The issue seems to be with invoking texture(...).rgb in-line, and is the same issue faced by this developer back in 2009 (New NV driver breaks my app (190.38) - I can’t post the link as I am a new user). The solution for them was to update to the latest drivers as it was a GLSL compile issue, however this did not solve the problem in my case as he has recently updated his drivers.

Any help with this would be greatly appreciated.


#2

Three possible ideas for you to consider.

First, as this thread (which refers to your specific error) indicates, calling texture() within conditionals is problematic as the partial derivatives are undefined. You can sample textures in conditionals, but you have to do something different to ensure that the partials are available for the filtering which occurs during sampling. This thread also describes an even simpler solution, where you just move the sampling outside of the conditional and then choose between the sampled values in the conditional.

Second, this thread, (which also refers to your specific error) indicates that this might be due to an oold GLSL bug in NVidia’s compiler, for which the work-around is listed. This might be the user’s problem, but my bet is on the first option (listed above). If it is their problem though, it sounds like the user can probably fix it just by updating their graphics driver to the latest version supported by their GPU, or the shader developer can work-around it using the work-around described in the thread.

And finally, the error may not be complaining about this, but a problem I notice in this shader code (in particular the lines flagging compiler errors) is that the code is trying to select a sampler from an array with a non-constant expression. Background on that below:

In older GLSLs (like v3.3.0), arrays of samplers can only be indexed with constant expressions. In more recent versions (apparently not available given this user’s GL drivers), this was expanded to dynamically uniform expressions.

However, in this shader, the expression used to index the array of samplers in lines 26 and 27 is neither of these.

What you probably should be using to support users on old GL drivers like this is array textures. These allow you to effectively choose which 2D texture slice to display using varyings (aka interpolators) which can vary for different vertices within a batch. See this wiki link: Array Texture.

Finally, it seems odd to me that this shader doesn’t have a #version line up-top. Shaders without it will be treated as version 1.1, and the shader listed is not a valid version 1.1 shader.


#3

Another consideration is to check GL_MAX_TEXTURE_IMAGE_UNITS, which is probably 32 on that hardware. You’ve declared 33 samplers in this shader, which may or may not actually be active (the driver could eliminate some depending on the current uniform state.) There’s no requirement for a driver to try to eliminate inactive samplers, though, so I’d conservatively expect that shader to fail to execute everywhere.


#4

I don’t think that this is the issue. Elements of an array of samplers don’t have to refer to distinct texture units. Even if the program tried to use more than 32 texture units, the error would occur in the client code when calling glActiveTexture(GL_TEXTURE0+i) with i>=32. The array size shouldn’t matter.

I’m fairly sure that this is down to indexing an array of samplers with a non-constant expression in 3.3. 4.0+ allows the use of dynamically-uniform expressions, which might be the case for some implementations. texID has the flat qualifier, so it will be constant for a single triangle. The specification states that an expression is dynamically uniform if it’s constant for an “invocation group” defined thus:

So even on 4.0+, this isn’t required to work, but it might.

As DarkPhoton suggests, array textures avoid this issue; the expression for the texture layer can be entirely dynamic. In addition, an array texture only occupies a single texture unit, which would avoid any issues with GL_MAX_TEXTURE_IMAGE_UNITS. On the other hand, all layers are part of the same texture and so have the same format, dimensions and sampling parameters (e.g. filter and wrap modes). GL_MAX_ARRAY_TEXTURE_LAYERS is required to be at least 256 (in 3.0 through 4.6).


#5

GL_MAX_ARRAY_TEXTURE_LAYERS is required to be at least 256 (in 3.0 through 4.6).

At least 2048 in GL 4.1+.


#6

[quote=“GClements, post:4, topic:103915”]
Elements of an array of samplers don’t have to refer to distinct texture units. Even if the program tried to use more than 32 texture units, the error would occur in the client code when calling glActiveTexture(GL_TEXTURE0+i) with i>=32. The array size shouldn’t matter.[/quote]

It’s common for GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS to be larger than GL_MAX_TEXTURE_IMAGE_UNITS, so you could successfully bind textures on 33 distinct units, and still be limited to 32 active units in the fragment shader.

It’s also common for drivers to do dead code elimination, and you might reasonably expect a shader compiler to figure out that colourTexture[0] and normalTexture[0] are dead, so there will never be more than 31 active samplers. You might also expect drivers to coalesce samplers set to to the same unit at draw time.

So, I don’t think this is the problem either, in the OP’s described failure case. But my point is that the specification doesn’t require dead code elimination, etc, so a shader author should not rely on a driver figuring this out. It could compliantly fail at link or draw time.


#7

Thank you all for your detailed replies, the issue was caused by trying to select a sampler from an array with a non-constant expression.

I haven’t heard of array textures, they seem useful and better suited to what I am trying to accomplish.

Thanks again for your help, I’ve learned a lot so far.


#8

While you’re thinking about this problem, you might read this wiki page as well:

It’s where you’ll likely be going after array textures.

Bindless textures are an even better solution than arrays in most cases, if your minimum GPU spec supports them. They have more advantages and fewer limitations.

With these, you can kick the legacy concept of the “texture unit” to the curb, and get rid of “bind to use” for textures.