Subroutines shaders


#1

Has anyone successfully done subroutines in glsl? It would be very helpful, especially for larger shaders.
I have some test code printing the Subroutine Uniforms just fine.

    int maxSub,maxSubU,activeS,countActiveSU;
    char name[256]; int len, numCompS;
    
    glGetIntegerv(GL_MAX_SUBROUTINES, &maxSub);
    glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubU);
    printf("Max Subroutines: %d  Max Subroutine Uniforms: %d\n", maxSub,maxSubU);
    
    glGetProgramStageiv(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &countActiveSU);
    
    for (int i = 0; i < countActiveSU; ++i) {
        
        glGetActiveSubroutineUniformName(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, i, 256, &len, name);
        
        printf("Suroutine Uniform: %d name: %s\n", i,name);
        glGetActiveSubroutineUniformiv(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, i, GL_NUM_COMPATIBLE_SUBROUTINES, &numCompS);
        
        int *s = (int *)malloc(sizeof(int) * numCompS);
        glGetActiveSubroutineUniformiv(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, i, GL_COMPATIBLE_SUBROUTINES, s);
        printf("Compatible Subroutines:\n");
        for (int j=0; j < numCompS; ++j) {
            
            glGetActiveSubroutineName(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, s[j], 256, &len, name);
            printf("\t%d - %s\n", s[j],name);
        }
        printf("\n");
        free(s);
    }

But I don’t know how to implement the subroutines.
I was trying
mBatch->getGlslProg()->uniform("Color", "ColorBlue");

Using this shader tutorial for source
http://www.geeks3d.com/20140701/opengl-4-shader-subroutines-introduction-3d-programming-tutorial/
it’s saying to
glUseProgram(program);
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1, &color_red_index);
where would I put that now?

solved!!!
indeed, I just had to put
auto shader = gl::context()->getGlslProg();
if( !shader ) return;
GLuint color_red_index = glGetSubroutineIndex(shader->getHandle(), GL_FRAGMENT_SHADER, “ColorBlue”);
glUseProgram(shader->getHandle());
glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &color_red_index);

This basically acts as a pointer function in glsl.


#2

Unfortunately, or fortunately, that is how you’d have to approach it, using vanilla gl. Glslprog doesn’t have subroutines wrapped. You’d basically want to do what you’ve written above exactly. On the glUseProgram line, you can either replace it with ScopedGlslProg… Or grab the id from the GlslProg and feed it to the glUseProgram.


#3

I’ve just been looking at subroutines myself, and this post was invaluable.
I was still getting exceptions when I was calling glGetSubroutineIndex on any valid shader, so for anyone in the same situation, I found the following to work.

Set your shader version to:

#version 400

& then request OpenGL 4.0 > in your App constructor, ie:

RendererGl(RendererGl::Options().version(4,0))

Everything then seems to work great.


#4

So I revisited subroutines and for some reason it doesn’t seem to be working again… which is odd cause I just reused the code from this original post.
I made two functions. The color_index is correct and everything prints out, it’s just always using the first subroutine for whatever reason.

void Flock::setupSubroutines(gl::BatchRef sBatch){
auto shader = sBatch->getGlslProg();
if( !shader ){ cout<<“No shader for subroute”<<endl; return;}

int maxSub,maxSubU,countActiveSU;
char name[256]; int len, numCompS;

glGetIntegerv(GL_MAX_SUBROUTINES, &maxSub);
glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubU);
printf("Max Subroutines: %d  Max Subroutine Uniforms: %d\n", maxSub,maxSubU);

glGetProgramStageiv(shader->getHandle(), GL_FRAGMENT_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &countActiveSU);

for (int i = 0; i < countActiveSU; ++i) {
    
    glGetActiveSubroutineUniformName(shader->getHandle(), GL_FRAGMENT_SHADER, i, 256, &len, name);
    
    printf("Suroutine Uniform: %d name: %s\n", i,name);
    glGetActiveSubroutineUniformiv(shader->getHandle(), GL_FRAGMENT_SHADER, i, GL_NUM_COMPATIBLE_SUBROUTINES, &numCompS);
    
    int *s = (int *)malloc(sizeof(int) * numCompS);
    glGetActiveSubroutineUniformiv(shader->getHandle(), GL_FRAGMENT_SHADER, i, GL_COMPATIBLE_SUBROUTINES, s);
    printf("Compatible Subroutines:\n");
    for (int j=0; j < numCompS; ++j) {
        
        glGetActiveSubroutineName(shader->getHandle(), GL_FRAGMENT_SHADER, s[j], 256, &len, name);
        printf("\t%d - %s\n", s[j],name);
    }
    printf("\n");
    free(s);
}

}
void Flock::setSubroutines(gl::BatchRef sBatch){
auto shader = sBatch->getGlslProg();
if( !shader ) return;
GLuint color_index = glGetSubroutineIndex(shader->getHandle(), GL_FRAGMENT_SHADER, “ColorTexture”);//ColorMarble ColorBlack ColorArtificial
glUseProgram(shader->getHandle());
glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &color_index);
}

I found this post and not sure if Cinder is calling useProgram somewhere and it’s stepping on it.
https://www.opengl.org/discussion_boards/showthread.php/177351-SOLVED-GLSL-subroutine-unselectable

also, I’m doing an Instanced draw call for this so maybe that is messing with it?


#5

Okay, I figured it out. Cinder’s Batch Draw functions do a couple gl::scopes which I believe reset the gl::useProgram. This would be a cinder developer solution or… for a temporary fix, just use the batch draw call manually.

auto ctx = gl::context();
gl::ScopedGlslProg ScopedGlslProg( mBatch->getGlslProg() );
cinder::gl::VaoRef mVao = mBatch->getVao();
gl::ScopedVao ScopedVao( mVao );
ctx->setDefaultShaderVars();
GLuint color_index = glGetSubroutineIndex(mBatch->getGlslProg()->getHandle(), GL_FRAGMENT_SHADER, "ColorWhite");
glUniformSubroutinesuiv( GL_FRAGMENT_SHADER, 1, &color_index);
gl::draw( mBatch->getVboMesh() );