Unspecified link error on ATI X1250 in shader sampling texture multiple times

Discussion created by trzy on Jan 12, 2011
Latest reply on Jan 12, 2011 by frali


I'm developing on a laptop with an integrated Radeon X1250 GPU. I'm writing a fragment shader to do bilinear interpolation manually (it's a long story, but absolutely necessary). This involves a fair amount of slow computations and four texture samples per fragment. The shader compiles, the log contains no errors, but the linker fails with this message:


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

Below is the complete shader. If I remove a single fetch, it links. Likewise, if I use constant texel coordinates for one of the fetches, like so:

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

It works as well. I've also tried manually inlining the FetchTexel() function to no avail. The program works fine on a desktop Nvidia machine. I'd like this code to run on lower spec hardware so I'm really hoping I'm not bumping into any sort of weird limitations here.

Thank you!


 * fragment.glsl
 * 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 Real3D 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(,texSize);      // clamp coordinates to within texture size
 mirror = mirrorEnable * mod(floor(,2.0); // whether this texel needs to be mirrored
 glTexCoord = ( mirror*(texSize-clampedCoord) +
     (vec2(1.0,1.0)-mirror)*clampedCoord +
     ) / 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
   * 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,,;
  c[1] = FetchTexel(uv[1],fsSubTexture.xy,,;
  c[2] = FetchTexel(uv[2],fsSubTexture.xy,,;
  c[3] = FetchTexel(uv[3],fsSubTexture.xy,,;

  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)
 // Apply translucency
 if (fsTransLevel < 1.0)
   gl_FragColor.a = fsTransLevel;
 // Apply ambient lighting
 gl_FragColor.rgb *= fsLightIntensity;