AMD GLSL "subtle" or "quiet" error.

Started by
11 comments, last by TheChubu 9 years, 9 months ago

My GLSL shader in my game engine has a very odd bug that my roomate found today. On his AMD graphics card any model that is skinned/animated is invisible. The models show up fine on my Nvidia card so this is ATI specific. The shaders compile fine on his card and there are no errors in the AMD GPU shader analyzer which is very odd.

We messed with this for a few hours and I found the block of code that causes the invisible models glitch. It's the part of the GLSL vertex shader that does the rotation for skinned meshes. Something here IS causing the problem. If I comment this out everything renders fine but nothing rotates obviously.

Here are all my uniforms, attributes, and varyings in the vertex shader:


uniform bool bRotation;
uniform vec4 modelPosition;
uniform vec4 modelRotation;
uniform float modelScale;
uniform bool bSkin;
uniform vec3 bonesOffset[64];
uniform vec3 bonesPos[64];
uniform vec4 bonesRot[64];
uniform vec4 sunLocation;
uniform vec3 sunDir;

varying vec3 halfVec;
varying vec3 eyeVec;
varying vec3 lightVec;

varying vec4 position;
varying vec3 normal;

attribute vec3 indices;
attribute vec3 weights;
attribute vec3 tangent;

varying vec4 nearShadowCoord;
varying vec4 farShadowCoord;

Here is the part of the vertex shader that is causing the problem:


		//do rotation
		i = 0;
		vec4 v = pos;
		vec4 rot[3];
		vec4 nor[3];
		
		while(i < 3) {
			vec4 r = bonesRot[int(indices[i])];
			rot[i] = vec4(quatRotation(vec3(v.x, v.y, v.z), r), 0.0f);
			nor[i] = vec4(quatRotation(normal, r), 0.0f);
			i++;
		}
		
		//Average the rotations by weight and apply them
		i = 0;
		vec4 final;
		normal = vec3(0.0f,0.0f,0.0f);
		while(i < 3) {
			final.x += (rot[i].x * weights[i]);
			final.y += (rot[i].y * weights[i]);
			final.z += (rot[i].z * weights[i]);
			normal.x += (nor[i].x * weights[i]);
			normal.y += (nor[i].y * weights[i]);
			normal.z += (nor[i].z * weights[i]);
			i++;
		}
		
		pos = final;

And here is the quatRotation() function that is used in the above code on lines 8 and 9:


vec3 quatRotation(vec3 v, vec4 r) {
			float q00 = 2.0f * r.x * r.x;
			float q11 = 2.0f * r.y * r.y;
			float q22 = 2.0f * r.z * r.z;
	
			float q01 = 2.0f * r.x * r.y;
			float q02 = 2.0f * r.x * r.z;
			float q03 = 2.0f * r.x * r.w;
	
			float q12 = 2.0f * r.y * r.z;
			float q13 = 2.0f * r.y * r.w;
	
			float q23 = 2.0f * r.z * r.w;
			vec3 f = vec3(0.0f,0.0f,0.0f);
			f.x = (1.0f - q11 - q22) * v.x + (q01 - q23) * v.y + (q02 + q13) * v.z;
			f.y = (q01 + q23) * v.x + (1.0f - q22 - q00) * v.y + (q12 - q03) * v.z;
			f.z = (q02 - q13) * v.x + (q12 + q03) * v.y + (1.0f - q11 - q00) * v.z;
			return f;
}

I have no idea what's actually wrong with my code. I develop on Nvidia and everything works great. I'm stumped D:

Advertisement

vec4 final;

isnt initialized so probably holds garbage, which you then add to. Plus Im not sure how you use the 4th component of final.

If nothing is rendered, most likely your shader program does not compile on ATI. Nvidia is known for more relaxed enforcement of correct GLSL code, therefor you might have an GLSL specific error which will be accepted by Nvidia but rejected by ATI. Best to log out the error messages after compiling your GLSL code, this will help you to quickly track down your error.

If nothing is rendered, most likely your shader program does not compile on ATI. Nvidia is known for more relaxed enforcement of correct GLSL code, therefor you might have an GLSL specific error which will be accepted by Nvidia but rejected by ATI. Best to log out the error messages after compiling your GLSL code, this will help you to quickly track down your error.

Good advice in general but the OP has already said:

The shaders compile fine on his card and there are no errors in the AMD GPU shader analyzer which is very odd.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


Good advice in general but the OP has already said:

Yes, I know, thought I always recommmend to double check the error messages on the GPU/driver it does not run on.


Something here IS causing the problem.

This must not be the issue. There are really many ways were a shader could break. Eg you use 3*64 registers for your bone data, if you comment out the presented section, the compiler will most likely remove 1*64 registers (for bonerot). This downgrad (~192 register -> ~128 register, try to produce the same effect by cutting down from 64 to 42 registers for all 3 arrays, just for testing purpose) could be enough to get your code running on (really) older hardware with limited registers. I just want to say, that uncommenting some shader code is not always a granted way to pin-down bugs in a shader.

Btw, what hardware are you using ?

If you can run it via PerfStudio, I'm pretty sure that'll let you inspect the draw call and maybe even an execution of the shader so you can see what is going on at runtime.

Your code makes my eyes hurt :)


        while(i < 3) {
            final.xyz += (rot[i].xyz * weights[i]);
            normal += (nor[i] * weights[i]);
            i++;
        }
NumberXaero is spot on. You're using an uninitialised variable to accumulate data. This is undefined behaviour and will most likely not work on most gpus

vec4 final;

isnt initialized so probably holds garbage, which you then add to. Plus Im not sure how you use the 4th component of final.

That was it. Some times you overlook the simplest things like that haha. That's kind of embarrassing. I do wonder why AMD let it complie and didn't even try and warn me about it though. I have all the error logging stuff on but the shader compiled without any actual errors.

Your code makes my eyes hurt smile.png

while(i < 3) {
final.xyz += (rot.xyz * weights);
normal += (nor * weights);
i++;
}

Didn't know you could write it that way. Thanks. I'm sure I can clean things up like that in a few places.

Yeah it's pretty amazing that both nVidia and AMD compiled without errors... Silently initializing to zero (nVidia) is just as bad as not initializing (AMD)...
I wonder if the Khronos GLSL reference compiler produces a warning/error?
On D3D I'm pretty sure an uninitialized variable will cause your shader to flat out fail to compile.

[edit] I just ran this test through the reference compiler, which should be the gold standard, the absolute truth:


void main()
{
  mediump vec4 result;// = vec4(0);
  gl_FragColor = result;
}

...and it happily compiles that code without warning you about the uninitialized variable...

With the initialization commented out, it produces


0:? Sequence
0:2  Function Definition: main( (void)
0:2    Function Parameters:
0:?     Sequence
0:5      move second child to first child (mediump 4-component vector of float)
0:5        'gl_FragColor' (fragColor mediump 4-component vector of float)
0:5        'result' (mediump 4-component vector of float)
0:?   Linker Objects

And with initialization it produces:


0:? Sequence
0:2  Function Definition: main( (void)
0:2    Function Parameters:
0:4    Sequence
0:4      Sequence
0:4        move second child to first child (mediump 4-component vector of float)
0:4          'result' (mediump 4-component vector of float)
0:4          Constant:
0:4            0.000000
0:4            0.000000
0:4            0.000000
0:4            0.000000
0:5      move second child to first child (mediump 4-component vector of float)
0:5        'gl_FragColor' (fragColor mediump 4-component vector of float)
0:5        'result' (mediump 4-component vector of float)
0:?   Linker Objects

What the hell GL, Y U NO ERROR?

[edit #2]

Scanning the spec, the only mention of uninitialized variables that I see is:

Global variables without storage qualifiers that are not initialized in their declaration or by the application will not be initialized by OpenGL, but rather will enter main() with undefined values.

So - uninitialized global variables are explicitly defined by the spec to have garbage values... but uninitialized local variables aren't even mentioned; they're not actually "undefined behavior", but instead they're just "unspecified"!

I assume this means that the vendors can do whatever the hell they want, and still be abiding by the specification... Why not do the right thing(tm)?

This topic is closed to new replies.

Advertisement