I recently stumbled upon a problem with if conditions in a GLSL fragment shader.
The simplest shader to reproduce is as follows:
uniform int dummy; // always set to 1 in the client application
void main()
{
vec4 colors[1];
colors[0] = vec4(0.0, 1.0, 0.0, 1.0);
int planeIndex = 0;
if(dummy > 0)
planeIndex = 0;
gl_FragColor = colors[planeIndex];
}
When I run this shader on a GeForce Go 7400 (OpenGL 2.1, GLSL 1.2, Shader Model 3) it crashes (the shader, not the card). When it’s run on Shader Model 4 hardware, everything is fine. Testing on SM 3 machines besides the mentioned one is not possible due to a lack of the appropriate hardware.
After some investigation it seems as if the use of planeIndex after the if statement is the important point.
In case you dare to run an unknown application, I have uploaded a little test tool that uses this shader. http://rapidshare.com/files/266569351/shadertest.7z
(blue is intial color, green after clicking means shader was successful, red means the shader failed to run)
Is this problem in any way known behavior or likely to be a platform/hardware error on my side?
Well, it seems as if the problem is some weird scope behavior. When I touch planeIndex in an if statement and try to access it afterwards, the shader fails. So the ternary operator should work flawlessly.
Thanks for your testing information. So up to now, all Shader Model 3 cards are red, all SM4 cards are green.
Thanks to an older catalyst driver on a Radeon I might have found the source of the problem. Instead of just crashing, the card gave me an error.
“Not supported temporary array indirect index”
It seems as if GLSL 1.2 does not allow indirect array indexing. On newer nVidia cards this problem is circumvented completely but on older cards (like my 7400 GO) it seems that using a non const int as an index basically works because the compiler can optimize the array access when the variable isn’t dynamically changed. But the if clause in the shader example is not resolvable during compile time, so there is now way for older cards to convert the indirect access into a direct one.
“But the if clause in the shader example is not resolvable during compile time, so there is now way for older cards to convert the indirect access into a direct one.”
#ifdef that block of code and supply the define via pre-source string. if GLSL <= 1.20 than alter the behavior.
“But the if clause in the shader example is not resolvable during compile time, so there is now way for older cards to convert the indirect access into a direct one.”
Also, IIRC “step( condition ) *” can be useful for masking if conditions from the compiler.
Form a vec4 as an array of floats, which have only 1 in the index ‘unit’ and 0 in other values. Than mix all gl_TextureMatrix’es using this coefficients… This vector can be constructed in the following way:
vec4 a = (0.0,1.0,2.0,3.0), b = vec4(0.01), c = vec4(unit)
vec4 kf = step(a-b, c) * step(c, a+b);