Jump to content
  • Advertisement
Sign in to follow this  
trzy

OpenGL Shader fails to link on ATI hardware, no error description

This topic is 2718 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I'm writing my own code to do bilinear interpolation with a GLSL v1.2 fragment shader. On my ATI X1250, it fails to link, and the program log only contains the following information:

"Fragment shader(s) failed to link, vertex shader(s) linked."

The log for the fragment shader says the shader was compiled successfully.

Here is my fragment shader:


#version 120

// Global uniforms
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
uniform vec3 polygonState; // .x=not defined, .y=not defined, .z=alpha processing enable

// Inputs from vertex shader
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
varying float fsLightIntensity; // lighting intensity

/*
* FetchTexel():
*
* Samples a single Model 3 texel from the selected texture, taking into
* account wrapping behavior.
*
* Computing normalized OpenGL texture coordinates (0 to 1) within the
* Real3D texture sheet:
*
* If the texture is not mirrored, we simply have to clamp the
* coordinates to fit within the texture dimensions, add the texture
* X, Y position to select the appropriate one, and normalize by 2048
* (the dimensions of the Real3D texture sheet).
*
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
*
* If mirroring is enabled, textures are mirrored every odd multiple of
* the original texture. To detect whether we are in an odd multiple,
* simply divide the coordinate by the texture dimension and check
* whether the result is odd. Then, clamp the coordinates as before but
* subtract from the dimension to mirror them:
*
* = [M*((w,h)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
* where M is 1.0 if the texture must be mirrored.
*/
vec4 FetchTexel(vec2 texCoord, vec2 texOffset, vec2 texSize, vec2 mirrorEnable)
{
vec2 clampedCoord, mirror, glTexCoord;

clampedCoord = mod(texCoord.st,texSize); // clamp coordinates to within texture size
mirror = mirrorEnable * mod(floor(texCoord.st/texSize),2.0); // whether this texel needs to be mirrored
glTexCoord = ( mirror*(texSize-clampedCoord) +
(vec2(1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;

return texture2D(textureMap, glTexCoord);
}

void main(void)
{
vec4 c[4];
vec2 uv[4], r;

if (fsTexParams.x == 0.0)
gl_FragColor = gl_Color; // if textures disabled, use color
else
{
/*
* Bilinear Filtering
*/

// Compute fractional blending factor, r, and lower left corner of texel 0
uv[0] = gl_TexCoord[0].st-vec2(0.5,0.5);
r = uv[0]-floor(uv[0]);
uv[0] = floor(uv[0]);

// Compute texel coordinates
uv[0] = uv[0] + vec2(0.5,0.5); // offset to within center of pixel
uv[1] = uv[0] + vec2(1.0,0.0);
uv[2] = uv[0] + vec2(0.0,1.0);
uv[3] = uv[0] + vec2(1.0,1.0);

// Fetch texels
c[0] = FetchTexel(uv[0],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[1] = FetchTexel(uv[1],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[2] = FetchTexel(uv[2],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);
c[3] = FetchTexel(uv[3],fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);

gl_FragColor = c[0]*(1-r.s)*(1-r.t) + c[1]*r.s*(1-r.t) + c[2]*(1-r.s)*r.t + c[3]*r.s*r.t;

/*
* T1RGB5:
*
* The transparency bit determines whether to discard pixels (if set).
* What is unknown is how this bit behaves when interpolated. OpenGL
* processes it as an alpha value, so it might concievably be blended
* with neighbors. Here, an arbitrary threshold is chosen.
*/
if (fsTexParams.y >= 0.0)
{
if (gl_FragColor.a > 0.0)
discard;
}
}

// Apply translucency
if (fsTransLevel < 1.0)
gl_FragColor.a = fsTransLevel;

// Apply ambient lighting
gl_FragColor.rgb *= fsLightIntensity;
}


The problem has to do with the four texture fetches into c[0], c[1], c[2], c[3], and c[4]. If I remove any one of these, the program works fine. Furthermore, if I change the line to fetch c[0] (or any other one) to:


c[0] = FetchTexel(vec2(0.0,0.0),fsSubTexture.xy,fsSubTexture.zw,fsTexParams.zw);


It works (but obviously with screwed up texels). What might the linker dislike about this program? What seems to be causing it to choke is passing a variable coordinate (uv[]) too many times. It appears to work fine on an Nvidia machine but as my main development machine is ATI-based, and because I'd like this to have a shot at running on lower-spec hardware, I'm very keen on getting it working.

Thanks.

Share this post


Link to post
Share on other sites
Advertisement

Does the shader compile properly ? Have you checked the error state after glCompileShader() ?


Compiles just fine and I've checked the logs to verify. Only the linker returns an error. Are there some limitations on ATI hardware concerning variables, arrays, or functions? That's what I think is causing this, ultimately.

Share this post


Link to post
Share on other sites

[quote name='ileonte' timestamp='1294802657' post='4757529']
Does the shader compile properly ? Have you checked the error state after glCompileShader() ?


Compiles just fine and I've checked the logs to verify. Only the linker returns an error. Are there some limitations on ATI hardware concerning variables, arrays, or functions? That's what I think is causing this, ultimately.
[/quote]

I guess the only way to tell for sure is to test the code on a different system with a different graphics card/set of drivers.

Share this post


Link to post
Share on other sites
If it doesn't give a proper error message, then it could be a driver issue.
The driver forum is here

http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=postlist&Board=13&page=1

Share this post


Link to post
Share on other sites
Well, the ATI forum was no help. They just said "the card is no longer supported so tough luck." (Paraphrasing here ;))

I found the problem and fixed it. For future reference, here is the solution:

Our friend Krypton here pointed out that some old ATI GPUs are limited to four passes of computations and texture reads. Bilinear filtering requires four texture samples, so that's really pushing it there. If texture coordinates were integers, I might be able to simplify the mirroring code, but alas, I'm stuck with GLSL1.2. As ugly as the equations are, I don't think anything can be factored out in the first texel to simplify the subsequent three computations. I spent a lot of time trying clever ideas, got close, but each time I found I needed to insert just one more computation per texel which broke things again. My last ditch attempt worked. I guessed the compiler might not be very smart and so I manually stuck pairs of texture coordinates into vec4's. I rearranged things slightly but, essentially, the program below is the same -- and it works!

I was also surprised that the original program happened to work fine on a crappy Intel GPU :P



vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
{
vec4 clampedCoord, mirror, glTexCoord;

clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
texOffset
) / 2048.0;

return glTexCoord;
}

...

/*
* Bilinear Filtering
*
* In order to get this working on ATI, the number of operations is
* reduced by putting everything into vec4's. uv_top holds the UV
* coordinates for the top two texels (.xy=left, .zw=right) and uv_bot
* is for the lower two.
*/

// Compute fractional blending factor, r, and lower left corner of texel 0
uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel
r = uv_bot.xy-floor(uv_bot.xy); // fractional part
uv_bot.xy = floor(uv_bot.xy); // integral part

// Compute texel coordinates
uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (may not be needed w/ GL_NEAREST)
uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors
uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);

// Compute the properly wrapped texel coordinates
uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));

// Fetch the texels and blend them
c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel)
c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right
c[2]=texture2D(textureMap,uv_top.xy); // top-left
c[3]=texture2D(textureMap,uv_top.zw); // top-right
gl_FragColor = c[0]*(1-r.s)*(1-r.t) + c[1]*r.s*(1-r.t) + c[2]*(1-r.s)*r.t + c[3]*r.s*r.t;

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!