Can't load more than one shading program

Okay, so here’s the problem. I have a C++ set of classes that load, compile, and link a vertex and a fragment shader to a program object. They all use OpenGL 1.5 ARB extensions, and when rendering to a single window, it seems as if I can change the shaders at will.

However, I’m working in Qt under MacOS, and when I create multiple QGLWidgets (OpenGL window panes, probably having their own context to render on), attempting to modify and even create the shading programs has some odd effects:

  1. If the shading objects are created during the initialization phase, and preloaded with the shaders, all work fine. If I attempt to modifiy the programs at this point, only the most recently created shader program is modified. Also, the program object for multiple (distinct) programs is always the same. ??

  2. If the shading objects are created after the initialization process, only the most recently created shader object works. Changing the shading program after creating the object does nothing.

  3. Neither deleting nor “deactivating” the shader program nor specific shaders changes any of the above functionality.

4.) Simply disabling the shader does not seem to return OpenGL to the ‘fixed’ pipeline.

The shaders themselves work perfectly. And so far as I can see, the code works perfectly too. No errors are thrown at any state. This is really irritating me, so any help would be greatly appreciated. Now for the long chunk of source code. :slight_smile:

// Shader.H
#ifndef ShaderH
#define ShaderH

#include <GLUT/glut.h>

class Shader
{
  private:
    char *readFile(const char *filename);

  protected:
    bool loaded;
    bool created;
    bool compiled;
    GLhandleARB shaderObject;
    
    void create(GLenum type);
    virtual GLenum getShaderType() = 0;
    		
  public:
    Shader(const char *filename=0);
   ~Shader();
   
    GLhandleARB getShader()  { return shaderObject; }
	 
    void open(const char *filename);
    
    friend class ShaderProgram;
};

#endif
// Shader.cpp
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

Shader::Shader(const char *filename)
{
  loaded=false;
  created=false;
  compiled=false;
  shaderObject=0;
  
  if (filename) open(filename);
}

Shader::~Shader()
{
  glDeleteObjectARB(shaderObject);
}

void Shader::create(GLenum type)
{
  if (created) return;
  
  shaderObject=glCreateShaderObjectARB(type);
  created=true;
}

char *Shader::readFile(const char *filename)
{
  ifstream file(filename);
  if (!file.is_open()) return 0;
  
  int size;
  char *program;
  file.seekg(0,ios_base::end);
  size=file.tellg();
  file.seekg(0,ios_base::beg);
  file.clear();
  
  program=new char[size+1];
  file.read(program,size);
  program[size]=0;
  return program;
}

void Shader::open(const char *filename)
{
  char *program;
  const GLcharARB **programPtr=(const GLcharARB **)&program;

  program=readFile(filename);
  if (program==0) return;
  if (shaderObject==0) create(getShaderType());
    
  glShaderSourceARB(shaderObject,1,programPtr,NULL);
  loaded=true;
  
  delete program;
  
  glCompileShaderARB(shaderObject);

  int status;
  glGetObjectParameterivARB(shaderObject,GL_OBJECT_COMPILE_STATUS_ARB,(GLint *)&status);
  if (!status)
    throw "Shader::exception(80):  shader did not compile!";
  compiled=true;
}
// FragmentShader.h
#ifndef FragmentShaderH
#define FragmentShaderH

#include "Shader.h"

class FragmentShader : public Shader
{
  private:
  protected:
    virtual GLenum getShaderType()          { return GL_FRAGMENT_SHADER_ARB; }
    
  public:
    FragmentShader(const char *filename=0) : Shader(filename) {}
    
};
// VertexShader.h
#ifndef VertexShaderH
#define VertexShaderH

#include "Shader.h"

class VertexShader : public Shader
{
  private:
  protected:
    virtual GLenum getShaderType()            { return GL_VERTEX_SHADER_ARB; }
    
  public:
    VertexShader(const char *filename=0) : Shader(filename) {}
};

#endif
// ShaderProgram.h
#ifndef ShaderProgramH
#define ShaderProgramH

#include "VertexShader.h"
#include "FragmentShader.h"

class ShaderProgram
{
  private:
    GLhandleARB programObject;
    VertexShader vertexShader;
    FragmentShader fragmentShader;
    
    bool vertexShaderUsed;
    bool fragmentShaderUsed;
    bool enabled;
    
    char *getInfoLog(GLhandleARB object);

    GLint getUniformLocation(const char *varName);
    
  protected:
  public:
    ShaderProgram(const char *vfile=0,const char *ffile=0);
   ~ShaderProgram();
   
    void loadVertexShader(const char *filename);
    void loadFragmentShader(const char *filename);
    void loadShaders(const char *vfile,const char *ffile);
   
    void enableVertexShader();
    void enableFragmentShader();
    void enableShaders();
   
    void enable();
    void disable();
   
    void disableVertexShader();
    void disableFragmentShader();
    void disableShaders();
   
    bool inline isEnabled()                  { return enabled; }
    GLhandleARB inline getProgramObject()    { return programObject; }

    void setUniform(const char *varName,int i1);
    void setUniform(const char *varName,int i1,int i2);
    void setUniform(const char *varName,int i1,int i2,int i3);
    void setUniform(const char *varName,int i1,int i2,int i3,int i4);
    void setUniform(const char *varName,float f1);
    void setUniform(const char *varName,float f1,float f2);
    void setUniform(const char *varName,float f1,float f2,float f3);
    void setUniform(const char *varName,float f1,float f2,float f3,float f4);

    void setUniformv(const char *varName,int size,int *ptr);
    void setUniformv(const char *varName,int size,float *ptr);
   
    void setUniformMatrix(const char *varName,int size,float *ptr);
};

#endif
//ShaderProgram.cpp
#include "ShaderProgram.h"

#include <iostream>
using namespace std;

ShaderProgram::ShaderProgram(const char *vfile,const char *ffile)
{
  programObject=glCreateProgramObjectARB();

  if (vfile) vertexShader.open(vfile);
  if (ffile) fragmentShader.open(ffile);
  
  vertexShaderUsed=false;
  fragmentShaderUsed=false;
  enabled=false;
}

ShaderProgram::~ShaderProgram()
{
  disableShaders();
  glDeleteObjectARB(programObject);
}

char *ShaderProgram::getInfoLog(GLhandleARB object)
{
  char *infoLog;
  int infoLogLength;
  
  glGetObjectParameterivARB(object,GL_OBJECT_INFO_LOG_LENGTH_ARB,(GLint *)&infoLogLength);
  
  infoLog=new char[infoLogLength+1];
  glGetInfoLogARB(object,infoLogLength,(GLsizei *)&infoLogLength,infoLog);
  return infoLog;
}

void ShaderProgram::loadShaders(const char *vfile,const char *ffile)
{
  loadVertexShader(vfile);
  loadFragmentShader(ffile);
}

void ShaderProgram::loadVertexShader(const char *filename)
{
  try
  {
    if (filename) vertexShader.open(filename);
  }
  catch (const char *error)
  {
    char *infoLog=getInfoLog(vertexShader.shaderObject);
    cout << error << endl;
    cout << infoLog << endl;
    delete infoLog;
  }
}

void ShaderProgram::loadFragmentShader(const char *filename)
{
  try
  {
    if (filename) fragmentShader.open(filename);
  }
  catch (const char *error)
  {
    char *infoLog=getInfoLog(fragmentShader.shaderObject);
    cout << error << endl;
    cout << infoLog << endl;
    delete infoLog;
  }
}

void ShaderProgram::enable()
{
  disable();
  glUseProgramObjectARB(programObject);
  enabled=true;
}

void ShaderProgram::enableShaders()
{
  enableVertexShader();
  enableFragmentShader();
}

void ShaderProgram::enableVertexShader()
{
  int status;
  disableVertexShader();
  glAttachObjectARB(programObject,vertexShader.shaderObject);
  glLinkProgramARB(programObject);

  glGetObjectParameterivARB(programObject,GL_LINK_STATUS,(GLint *)&status);
  if (status==GL_FALSE)
    throw "ShaderProgram::error(103)::  shader program did not link!";
  
  vertexShaderUsed=true;
}

void ShaderProgram::enableFragmentShader()
{
  disableFragmentShader();
  glAttachObjectARB(programObject,fragmentShader.shaderObject);
  glLinkProgramARB(programObject);
  fragmentShaderUsed=true;
}

void ShaderProgram::disable()
{
  glUseProgramObjectARB(0); 
  if (enabled) enabled=false;
}

void ShaderProgram::disableShaders()
{
  disableVertexShader();
  disableFragmentShader();
}

void ShaderProgram::disableVertexShader()
{
  if (vertexShaderUsed)
  {
    glDetachObjectARB(programObject,vertexShader.shaderObject);
    vertexShaderUsed=false;
  }
}

void ShaderProgram::disableFragmentShader()
{
  if (fragmentShaderUsed)
  {
    glDetachObjectARB(programObject,fragmentShader.shaderObject);
    fragmentShaderUsed=false;
  }
}

GLint ShaderProgram::getUniformLocation(const char *varName)
{
  GLint loc=glGetUniformLocationARB(programObject,varName);
  cout << "loc = " << loc << endl;
  return loc;
}

void ShaderProgram::setUniform(const char *varName,int i1)
{
  GLint loc=getUniformLocation(varName);
  glUniform1iARB(loc,i1);
}

// other setUniform function clipped for space
// ShaderWidget.h
#ifndef ShaderWidgetH
#define ShaderWidgetH

#include <QtGui>
#include <QtOpenGl>

#include "ShaderProgram.h"

class ShaderWidget : public QGLWidget
{
  Q_OBJECT;

  private:
  int layer;

  ShaderProgram *program;

  GLuint contour;
  GLuint volume;
  GLuint colors;

  protected:
    void paintGL();
    void initializeGL();
    void resizeGL(int width,int height);

  public:
    ShaderWidget(QWidget *parent=0);    
   ~ShaderWidget();
   
    void loadContour(int layer);
    void loadVolume(int layer);
    void setColors();
    
    void loadShaders(const char *vname,const char *fname);
};

#endif
// ShaderWidget.h
#include "ShaderWidget.h"

#include <iostream>
#include <fstream>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include "ShaderProgram.h"

using namespace std;

ShaderWidget::ShaderWidget(QWidget *parent) : QGLWidget(parent)
{
  layer=0;
  program=0;
  contour=0;
  volume=0;
  colors=0;
}

ShaderWidget::~ShaderWidget()
{
  glDeleteTextures(1,&contour);
  glDeleteTextures(1,&volume);
  glDeleteTextures(1,&colors);
  delete program;
}

void ShaderWidget::loadContour(int layer)
{
  char slice[512*512*4];
  int *intSlice=(int *)slice;
  
  ifstream file("../../32_cubes.dat",ios::binary);
  
  // load the 100th axial slice
  file.seekg(6+512*512*4*layer,ios_base::beg);
  file.read(slice,512*512*4);
  file.close();
    
  if (!contour) glGenTextures(1,&contour);
  glActiveTextureARB(GL_TEXTURE1_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,contour);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,512,512,0,GL_RGBA,GL_UNSIGNED_BYTE,slice);
}

void ShaderWidget::loadVolume(int layer)
{  
  char slice[512*512*2];
  short *ptr=(short *)slice;
  
  ifstream file("../../CTscan.dat",ios::binary);
  
  // load the 100th axial slice
  file.seekg(6+512*512*2*layer,ios_base::beg);
  file.read(slice,512*512*2);
  file.close();
  
  for (int i=0;i<512*512;i++)
  {
    ptr[i]+=1000;
    if (ptr[i]<0) ptr[i]=0;
    if (ptr[1]>4095) ptr[i]=4095;
    ptr[i]<<=4;
  }
    
  if (!volume) glGenTextures(1,&volume);
  glActiveTextureARB(GL_TEXTURE0_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D,volume);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
  
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16,512,512,0,GL_LUMINANCE,GL_UNSIGNED_SHORT,slice);
}

void ShaderWidget::setColors()
{
  char color[32*4] =
  {
     64,  0,  0,255,    128,  0,  0,255,      192,  0,  0,255,      255,  0,  0,255,
      0, 64,  0,255,      0,128,  0,255,        0,192,  0,255,        0,255,  0,255,
      0,  0, 64,255,      0,  0,128,255,        0,  0,192,255,        0,  0,255,255,
     64, 64,  0,255,    128,128,  0,255,      192,192,  0,255,      255,255,  0,255,
     64,  0, 64,255,    128,  0,128,255,      192,  0,192,255,      255,  0,255,255,
      0, 64, 64,255,      0,128,128,255,        0,192,192,255,        0,255,255,255,
    255, 64, 64,255,    192,128,128,255,      128,192,192,255,       64,255,255,255,
     64,255, 64,255,    128,194,128,255,      192,128,192,255,      255, 64,255,255,
  };
  
  glGenTextures(1,&colors);
  glActiveTextureARB(GL_TEXTURE3_ARB);
  glEnable(GL_TEXTURE_1D);
  glBindTexture(GL_TEXTURE_1D,colors);
  glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
  glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

  //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

  glTexImage1D(GL_TEXTURE_1D,0,GL_RGBA,32,0,GL_RGBA,GL_UNSIGNED_BYTE,color);
}

void ShaderWidget::paintGL()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  
  if (!program) return;
	
  program->enable();

  program->setUniform("brightness",0.0f);
  program->setUniform("contrast",1.0f);
  program->setUniform("contourData",1);
  program->setUniform("contourMix",2);
  program->setUniform("colorMap",3);

  glTranslatef(0.0,0.0,-2.0);
  glScalef(2.0,2.0,2.0);
  glBegin(GL_QUADS);
  {
    glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0.0,1.0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0.0,1.0);
    glMultiTexCoord1fARB(GL_TEXTURE3_ARB,0.0);
    glVertex3f(-1.0,-1.0,0.0);

    glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1.0,1.0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1.0,1.0);
    glMultiTexCoord1fARB(GL_TEXTURE3_ARB,1.0);
    glVertex3f( 1.0,-1.0,0.0);

    glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1.0,0.0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1.0,0.0);
    glMultiTexCoord1fARB(GL_TEXTURE3_ARB,1.0);
    glVertex3f( 1.0, 1.0,0.0);

    glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0.0,0.0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0.0,0.0);
    glMultiTexCoord1fARB(GL_TEXTURE3_ARB,0.0);
    glVertex3f(-1.0, 1.0,0.0);
  }
  glEnd();
  //glFlush();
  
  program->disable();
  
  glTranslatef(0.0,0.0,0.5);

  // The below shape shows up as a black triangle!
  glBegin(GL_TRIANGLES);
  {
    glColor3f(1.0,0.0,1.0);
    glVertex3f(0.0,0.0,0.0);
    
    glColor3f(0.0,1.0,0.0);
    glVertex3f(1.0,0.0,0.0);
    
    glColor3f(0.0,0.0,1.0);
    glVertex3f(1.0,1.0,0.0);
  }
  glEnd();
  glFlush();
}

void ShaderWidget::resizeGL(int width,int height)
{
  glViewport(0,0,width,height);
  glClearColor(0,0,0,0);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-1.5,1.5,-1.5,1.5,0.9,10.0);  
}

void ShaderWidget::initializeGL()
{
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  
  loadVolume(0);
  loadContour(0);
  setColors();

  /*
  program=new ShaderProgram;

  try
  {
    program->loadShaders("../../toon.vert","../../toon.frag");
    program->enableShaders();
  }
  catch (const char *err)
  {
    std::cout << err << std::endl;
  }
  */
}

void ShaderWidget::loadShaders(const char *vname,const char *fname)
{
  if (program) delete program;
  program=new ShaderProgram;

  //if (!program) return;
  //program->disableShaders();
  //program->disable();

  try
  {
    program->loadShaders(vname,fname);
    program->enableShaders();
  }
  catch (const char *err)
  {
    std::cout << err << std::endl;
  }
  
  program->enable();
}
// main.cpp
#include <iostream>
#include <QtGui>
#include <QtOpenGl>

#include "ShaderWidget.h"

using namespace std;

int main(int argc,char **argv)
{
  QApplication app(argc,argv);
  QWidget window;
  ShaderWidget left,middle,right;
  QHBoxLayout layout;
  
  layout.addWidget(&left);
  layout.addWidget(&middle);
  layout.addWidget(&right);
  
  left.show();
  middle.show();
  right.show();
  
  window.setLayout(&layout);

  window.resize(400,400);
  window.show();

  left.loadShaders("../../conmap.vert","../../conmap.frag");
  middle.loadShaders("../../conmap.vert","../../conmap.frag");
  right.loadShaders("../../conmap.vert","../../conmap.frag");
  
  return app.exec();
}

Sorry about the length, but I don’t want any confusion about what my code does and doesn’t do. (Oh, and if you find any syntactic errors, it was from copy/paste, and I can assure you, it does compile.) Thanks in advance for anyone brave enough to read through all this!

You should insert calls to the glGetError(). This might give you some errors :slight_smile:

I do not know how this works on the MacOS however on Windows the OGL context is associated with thread so if application is single threaded and uses multiple contexts, it needs to switch the contexts. It is possible that each widget sets its own context when it needs to do something (e.g. redraw itself) so, if you do something outside such calls, you will use context that was set by last widget that need to use it which might be not the one that you wish to modify.

Ahh, thanks for reminding me! This is actually to test a much larger program, which I did exactly what you said. When I created the shaders in initializeGL, I got no errors from glGetError. When I created it outside the function, I get errors trying to enable ANY of the progams except the one which belongs to the most recently created widget. Specifically “INVALID VALUE” errors. Making calls to glIsProgram (OpenGL 2.0 is available to me…I just want to maintain some backward compatibility), it told me that the program objects didn’t exist, except for the most recently created.

But thanks for reminding me. I had a feeling I forgot something in my post. :slight_smile:

From such errors I would think that is almost certain that the programs ended in different contexts and you need to be in correct context to use/change one of them.

I do not know if display list sharing also shares the programs however you can try it.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.