Linking multiple glsl source files into one program

Sage
Posts: 1,199
Joined: 2004.10
Post: #1
Right now, I'm not certain if it's my GLSL code that's to blame, or my C++ code calling gl functions to attach multiple glsl source files into one program.

Anyway, my situation is that I want to define a few useful functions in a single glsl file. Then, other glsl files, which define a main(), can call those functions.

What I'm not certain about is whether I'm linking the two files ( into one program ) incorrectly, of if I'm defining my functions ( in the "library" file ) incorrectly.

First, the error I'm getting is this:
"ERROR: 0:14: 'GC_blurredFragment' : no matching overloaded function found"

Where, "GC_blurredFragment" is a function defined in the file "gaussian_combiner_lib.fs":

gaussian_combiner_lib.fs:
Code:
uniform float downsampleFactor;
uniform sampler2DRect srcImage;
uniform sampler2DRect blurredImage;

vec4 GC_blurredFragment( void )
{
    return texture2DRect( blurredImage, gl_FragCoord.st / downsampleFactor );    
}

vec4 GC_sourceFragment( void )
{
    return texture2DRect( srcImage, gl_FragCoord.st );
}

And here's the file, "bloom_mix.fs" which calls those functions:
Code:
uniform float alpha;
uniform float lumaScale;
uniform float lumaPower;
uniform float lumaMin;
uniform float lumaMax;

void main( void )
{
    vec3 src = GC_blurredFragment().rgb;
    vec3 dst = GC_sourceFragment().rgb;
    
    //Get luminance
    float luma = dot( vec3( 0.299, 0.587, 0.114 ), src );    

    //Ramp luma from lumaMin to lumaMax, raise it to power, and multiply it by lumaScale
    luma = min( pow( smoothstep( lumaMin, lumaMax, luma ), lumaPower ) * lumaScale, 1.0 );
    
    //Now ramp from dst to ( dst + luma ) by alpha
    gl_FragColor = vec4( mix( dst, dst + vec3( luma ), alpha ).rgb, 1.0 );
}

My code which attaches the two files to one program is here:
Code:
_mixProgram.attachFile( PATH( shaderFolder() + "gaussian_combiner_lib.fs" ), glsl::Program::Fragment );
_mixProgram.attachFile( PATH( shaderFolder() + _mixProgramPath ), glsl::Program::Fragment );
_mixProgram.link();

The implementation of the method glsl::Program::attachFile ( and dependant other methods ) is here:
Code:
void Program::attachFile( const std::string &file, shader_type type )
{
    try
    {
        attachString( read( file ), type );
    }
    catch ( const Exception &e )
    {
        throw Exception( "error compiling file: \"" + file + "\"", e.error );
    }
}


void Program::attachString( const std::string &programString, shader_type type )
{
    glError();

    const char *dataStr = programString.c_str();

    GLhandleARB shader = 0;
    switch( type )
    {
        case Vertex: shader = glCreateShaderObjectARB( GL_VERTEX_SHADER_ARB ); break;
        case Fragment: shader = glCreateShaderObjectARB( GL_FRAGMENT_SHADER_ARB ); break;
    }
    
    glShaderSourceARB( shader, 1, (const GLcharARB **)(&dataStr), NULL );

    std::string error;
    if ( !compile( shader, error ))
    {
        throw Exception( "compile error", error );
    }
    
    if ( !_program )
    {
        _program = glCreateProgramObjectARB();    
    }
    
    glError();

    glAttachObjectARB( _program, shader );    
    glDeleteObjectARB( shader );

    glError();
}


void Program::link( void )
{
    glError();

    glLinkProgramARB( _program );
    
    std::string error;
    if ( infoLog( _program, error ))
    {
        throw Exception( "link error", error );
    }

    GLint linked;
    glGetObjectParameterivARB( _program, GL_OBJECT_LINK_STATUS_ARB, &linked );

    _linked = linked != 0;

    glError();
}

std::string Program::read( const std::string &filename )
{
    using namespace std;

    fstream file( filename.c_str(), ios::in | ios::binary | ios::ate );
    long size = file.tellg();
    file.seekg(0, ios::beg);

    if (size <= 0)
    {
        throw Exception( "glsl::Program::read", "Couldn't open file: \"" + filename + "\"" );
    }
    
    char *buf = new char[size + 1];
    memset(buf, '\0', size + 1); //be damn certain about null terminator
    
    file.read(buf, size);
    file.close();
    
    string text(buf);
    delete [] buf;

    return text;
}


bool Program::infoLog( GLhandleARB obj, std::string &log )
{
    GLint infologLength = 0;
    GLsizei charsWritten  = 0;
    char *infoLog;
    
    glGetObjectParameterivARB( obj, GL_OBJECT_INFO_LOG_LENGTH_ARB,
                               &infologLength);
    
    if (infologLength > 0)
    {
        infoLog = new char[ infologLength ];

        glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog);

        log = infoLog;
        delete [] infoLog;
        
        return true;
    }
    
    return false;
}

So, can anybody help me? Is it even possible to define a "library" which defines methods which other files can call?

For what it's worth, my glsl code works if I define it all in one file, I just want to eliminate redundant code... since I have a lot of filters which do the same stuff and might as well recycle one library of functions.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #2
can you not just read in both files, concatenate them and then send that to be compiled as a shader program?

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #3
Technically, yes, but I was under the impression that GLSL's attach/compile/link cycle was designed for exactly this sort of usage...
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
Unfortunately, linking multiple shaders together into one program is not often done, and therefore not well-tested. When I tried to do this originally, I had serious issues on both NVidia/Linux and Mac OS X. Unfortunately, I no longer have access to the bug number to know whether that particular issue was fixed... though my recollection is that it was.

I've written code for linking multiple shaders into one program here: http://onesadcookie.com/trac/browser/Kiloplane/shader.c though I don't know if I've ever tested it. Might be worth a look, anyway.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #5
Well, your code's doing the same thing as mine, as far as I can tell. The only difference is that I free my shader object immediately after attaching it to the program, whereas you free it when the program is deleted. I'd be surprised if that makes any difference.

Well, I'm just going to have to live with cut-n-paste boilerplate... Rasp
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #6
Oh, I think I know what your problem might be -- a lack of a function prototype for the functions declared in other shaders Smile
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  OpenAL - Ogg files vs Caf files Fred9000 8 14,141 Aug 23, 2011 08:01 PM
Last Post: ipeku
  ios/mac shader - shared glsl source OptimisticMonkey 2 5,264 Jun 17, 2011 08:59 AM
Last Post: OptimisticMonkey
  Multiple render targets in GLSL... glFragData[] TomorrowPlusX 5 12,727 Oct 17, 2008 01:36 PM
Last Post: OneSadCookie
  GLSL and multi file linking TomorrowPlusX 4 3,396 Jun 16, 2008 03:48 PM
Last Post: OneSadCookie
  Problem linking GLSL Ilyasim 4 3,736 Dec 18, 2007 12:46 PM
Last Post: OneSadCookie