Odd GLSL loop breakout behavior

Pretty sure this is a bug but thought I’d verify here first. Maybe this is undefined behavior.

This is what I tried (simple “find the correct bin” algorithm):

int i;
for ( i = 0; i < f_array.length(); i++ )
  if ( myval >= f_array[i] )
    break;

The problem is that on loop termination i is +1 the value it should be.

The attached test program demonstrates. As-is, it renders a red teapot (should be green). If you uncomment the #define ACTIVATE_KLUDGE then it activates a hacked loop that correctly renders green.

#include <stdio.h>
#include <stdlib.h>
#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

//#define ACTIVATE_KLUDGE

static const char Vertex_src[] = 
  "void main(void) { gl_Position=ftransform(); }";

static const char Fragment_src[] = 
"#version 120                                                            
"
" void main(void)                                                        
"
" {                                                                      
"
"   float f[] = float[]( 3., 2., 1. );                                   
"
"                                                                        
"
#ifndef ACTIVATE_KLUDGE
"   // FIXME: This doesn't work (NVidia 195.36.08)                       
"
"   int i;                                                               
"
"   for ( i = 0; i < f.length(); i++ )                                   
"
"     if ( 3.5 >= f[i] )                                                 
"
"       break;                                                           
"
"                                                                        
"
#else
"   // FIXME: Kludge around suspected NVidia GLSL bug                    
"
"   int i, val = 0;                                                      
"
"   for ( i = 0; i < f.length(); i++ )                                   
"
"     if ( 3.5 >= f[i] )                                                 
"
"     {                                                                  
"
"       val = i;                                                         
"
"       break;                                                           
"
"     }                                                                  
"
"   i = val;                                                             
"
#endif
"                                                                        
"
"   gl_FragColor = i == 0 ? vec4(0.,1.,0.,1.) : vec4(1.,0.,0.,1.);       
"
" };                                                                     
";


void keybd ( unsigned char, int, int )
{
  exit ( 0 ) ;
}


void reshape(int wid, int ht)
{
  glViewport(0, 0, wid, ht);
}

void showGLerror ()
{
  GLenum err ;

  while ( (err = glGetError()) != GL_NO_ERROR )
    fprintf ( stderr, "OpenGL Error: %s
", gluErrorString ( err ) )  ;
}


void display ( void )
{
  static float a = 0.0f ;

  a += 0.3f ;

  glMatrixMode      ( GL_PROJECTION ) ;
  glLoadIdentity    () ;
  glFrustum         ( -1.0f, 1.0f,
                      -1.0f / (640.0f/480.0f), 1.0f / (640.0f/480.0f),
                      3.0f, 10.0f) ;

  glMatrixMode      ( GL_MODELVIEW ) ;
  glLoadIdentity    () ;
  glTranslatef      ( 0.0, 0.0, -5.0 ) ;
  glRotatef         ( a, 0.2, 0.7, 0 ) ;

  glEnable          ( GL_DEPTH_TEST ) ;
  glEnable          ( GL_CULL_FACE  ) ;
  glCullFace        ( GL_FRONT ) ;

  glClearColor      ( 0.0f, 0.0f, 0.0f, 1.0f ) ;
  glClear           ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;

  glutSolidTeapot   ( 1.0f ) ;

  glutSwapBuffers   () ;
  glutPostRedisplay () ;
}


void showShaderInfo ( const char *what, GLhandleARB handle )
{
  int len = 0 ;

  showGLerror () ;
  glGetObjectParameterivARB ( handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &len ) ;
  showGLerror () ;

  if ( len > 0 )
  {
    int trueLen ;
    char *s = new char [ len ] ;

    glGetInfoLogARB ( handle, len, &trueLen, s ) ;

    if ( trueLen > 0 && s [ 0 ] != '\0' )
      fprintf ( stderr, "%s:
%s
", what, s ) ;

    delete [] s ;
  }
}



GLhandleARB installShader ( const char *src, GLenum type )
{
  const char *type_str = type == GL_VERTEX_SHADER ? "vertex" : "fragment";

  GLhandleARB handle = glCreateShaderObjectARB ( type ) ;

  glShaderSource         ( handle, 1, &src, 0 ) ;
  glCompileShader        ( handle ) ;

  GLint stat ;
  glGetObjectParameterivARB ( handle, GL_OBJECT_COMPILE_STATUS_ARB, & stat ) ;
  showShaderInfo ( type_str, handle ) ;

  if ( ! stat )
  {
    fprintf ( stderr, "Failed to compile %s shader.
", type_str );
    exit ( 1 ) ;
  }

  return handle ;
}


GLhandleARB linkShaders ( GLhandleARB vsHandle, GLhandleARB fsHandle )
{
  GLint stat ;
  GLhandleARB handle = glCreateProgramObjectARB () ;

  glAttachObjectARB         ( handle, vsHandle ) ;
  glAttachObjectARB         ( handle, fsHandle ) ;
  glLinkProgramARB          ( handle ) ;
  glGetObjectParameterivARB ( handle, GL_OBJECT_LINK_STATUS_ARB, &stat ) ;
  showShaderInfo ( "Linking", handle ) ;

  glValidateProgram      ( handle ) ;
  showShaderInfo ( "Validate", handle ) ;

  if ( ! stat )
  {
    fprintf ( stderr, "Failed to link shader.
" ) ;
    exit ( 1 ) ;
  }

  return handle ;
}


int main ( int argc, char **argv )
{
  // Init GL context
  glutInit            ( &argc, argv ) ;
  glutInitDisplayMode ( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE ) ;
  glutInitWindowSize  ( 500, 500 ) ;
  glutCreateWindow    ( "Shader Test" ) ;
  glutDisplayFunc     ( display  ) ;
  glutKeyboardFunc    ( keybd    ) ;
  glutReshapeFunc     ( reshape  ) ;

  // Load and compile shaders
  printf( "Compiling vertex shader...
" );
  GLhandleARB vsHandle = installShader ( Vertex_src, GL_VERTEX_SHADER  );
  printf( "Compiling fragment shader...
" );
  GLhandleARB fsHandle = installShader ( Fragment_src, GL_FRAGMENT_SHADER);

  // Link shaders
  printf( "Linking...
" );
  GLhandleARB handle   = linkShaders ( vsHandle, fsHandle ) ;

  // Activate shader
  glUseProgramObjectARB ( handle ) ;

  // Draw with shader
  glutMainLoop () ;
  return 0 ;
}

Your comment says this is an NVIDIA bug. Have you tested it on our (AMD’s) implementation? Does it work as expected?

No, not yet. One of our guys is working on an AMD port of our app in the background though.