Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 13

Thread: Generating pipelines, attaching shaders: working code anyone?

  1. #1
    Intern Contributor
    Join Date
    Sep 2012
    Posts
    85

    Generating pipelines, attaching shaders: working code anyone?

    Greetings:
    I am using core 4.3. Masochistic maybe but your help would be appreciated with the following:-)

    I have two different geometric objects that need to be transformed differently. I know I can do this in one vertex shader with a conditional on a uniform.

    But I am curious how to do this by setting up two separate pipelines, one with a VS to handle obj1 and the other a VS to handle obj2. The fragment shader would be common pass-through. So, I would bind pipeline1 prior to drawing obj1 and pipeline2 prior to drawing obj2.

    In a short section on separate shader objects the red book 8th ed. indicates use of
    glCreateShaderProgramv() to make a sharable program for a shader, then glGenProgramPipelines() and glBindProgramPipeline() to create/manage pipelines and
    glUseProgramStages() to attach shader stages to the pipeline.

    Then there's a bit about "The built-in gl_PerVertex block must be redeclared in shaders" which I don't understand at all.

    I would really appreciate it if someone kindly shares simple working code illustrating the above or explains the process step by step. (The RB site has nothing yet and google didn't turn up much code.)

    Thanks in advance,
    Sam

  2. #2
    Senior Member OpenGL Pro
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    1,135
    Quote Originally Posted by sam_thedancer
    [..]core 4.3. Masochistic maybe[..]
    Care to elaborate why? O_o

    I have to admit, I wasn't aware of it either but the GLSL spec states:

    Quote Originally Posted by The GLSL Spec
    The gl_PerVertex block can be redeclared in a shader to explicitly indicate what subset of the fixed pipeline interface will be used.
    EDIT: I was wrong before. Having tried it myself using separable programs, I can conclude that at least on AMD (and IIRC groovounet also stated on his website that NV is picky here too), you need to redeclare gl_PerVertex. Please refer to this thread for an exhaustive and comparably exhausting bug report.
    Last edited by thokra; 04-22-2013 at 08:45 AM.

  3. #3
    Junior Member Regular Contributor
    Join Date
    Mar 2009
    Posts
    153
    Yes, when using separable programs you have to declare gl_PerVertex block. NV driver also (correctly) requires it.

  4. #4
    Senior Member OpenGL Pro
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    1,135
    Anyway, as requested, here's some working, albeit horribly useless, code:

    Code :
    GLuint pipeline;
     
    void createProgramPipeline()
    {
        gl::GenProgramPipelines(1, &pipeline);
        GLuint vShader = gl::CreateShaderProgramv(gl::VERTEX_SHADER, 1, &vSrc);
        GLuint gShader = gl::CreateShaderProgramv(gl::GEOMETRY_SHADER, 1, &geomSrc);
        GLuint fShader = gl::CreateShaderProgramv(gl::FRAGMENT_SHADER, 1, &fSrc);
     
        gl::UseProgramStages(pipeline, gl::VERTEX_SHADER_BIT, vShader);
        gl::UseProgramStages(pipeline, gl::GEOMETRY_SHADER_BIT, gShader);
        gl::UseProgramStages(pipeline, gl::FRAGMENT_SHADER_BIT, fShader);
     
        // if you don't use explicit uniform locations you still need to query the location
        GLint loc = gl::GetUniformLocation(fShader, "color");
     
        // example use of DSA-style uniform interface
        gl::ProgramUniform4f(fShader, loc, 1.f, 0.f, 0.f, 1.f);
     
        // if you want to use classic uniform calls, you first need to select the correct separate program
        // gl::ActiveShaderProgram(pipeline, fShader);
        // gl::Uniform4f(loc, 1.f, 0.f, 0.f, 1.f);
     
        GLchar log[2048];
        gl::GetProgramInfoLog(vShader, 2048, NULL, log);
        printLog (log);
        gl::GetProgramInfoLog(gShader, 2048, NULL, log);
        printLog (log);
        gl::GetProgramInfoLog(fShader, 2048, NULL, log);
        printLog (log);
        gl::GetProgramPipelineInfoLog(pipeline, 2048, NULL, log);
        printLog (log);
     
        // to use the pipeline call the following - note that if glUseProgram() was called with a valid program object before, BindProgramPipeline has NO effect!
        // call glUseProgram(0) if you're unsure
        gl::BindProgramPipeline(pipeline);
     
    }

    Two things:

    • don't get distracted by the use of the gl namespace. It's because I'm using Alfonse's glLoadGen which can generate namespace encapsulated code.
    • note that there are no gl::BindAttribLocation or gl::BindFragDataLocation calls anywhere. Why? Simple: You cannot call the functions anymore because you don't explicitly link a seperable program - it's compiled, linked and validated automatically when calling CreateShaderProgramv()! However, BindFragDataLocation and BindAttribLocation need to be called before linking or else the calls will have no effect. The solution: explicit attribute locations like so:
      Code :
      // vertex shader
      layout(location = 0) in vec 4 whatever; // the index of the generic attribute [I]whatever [/I]is now 0
       
      // fragment shader
      layout(location = 0) out vec4 RenderTarget1; // output FragColor written to draw buffer 0 - COLOR_ATTACHMENT0
      layout(location = 1) out vec4 RenderTarget2; // output FragColor written to draw buffer 1 - possibly COLOR_ATTACHMENT1


    EDIT: While the above is true, i.e. using explicit attrib locations instead of glBind{Attrib|FragData}Location(), I forgot to mention one thing: If not specified with the aforementioned call or explicitly defined in the shader, the linker will assign locations which can be retrieved with glGet{Attrib|FragData}Location(). However, since ARB_separate_shader_objects depends on ARB_explicit_attrib_location you should do it the new way and just be explicit. The SGI-inspired(or mandated) query stuff was and is simply cumbersome.

    EDIT2: As I understand it, you could theoretically avoid the above "shortcomings" of using glCreateShaderProgramv() by simply rewriting the equivalent code section to include the binding of attrib locations using the API. All in all, the only difference compared to the normal flow (and the bit that makes the separable linkage possible) is the addition of

    Code :
    glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);

    before actually linking the program. One could ammend the code by doing stuff like this:
    Code :
    glAttachShader(program, shader);
     
    // bind attribs here
    glBindAttribLocation(....)/glBindFragDataLocation(....)
    ....
     
    glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
    glLinkProgram(program);
    glDetachShader(program, shader);

    At least I theorize that this should work. Issue #15 in ARB_separate_shader_objects addresses this as well, but seems to use the API from EXT_seperate_shader_objects - which doesn't match the core API.
    However, I'm still of the opinion that the benefit of using glCreateShaderProgramv() and explicit locations outweighs the benefit of assigning attrib locations via API.
    Last edited by thokra; 04-23-2013 at 08:12 AM.

  5. #5
    Intern Contributor
    Join Date
    Sep 2012
    Posts
    85
    Thanks a million, Thokra.
    I am going to try then to write a minimal program setting up a pipeline to draw a triangle. If it doesn't work I hope you won't mind taking a look.

  6. #6
    Senior Member OpenGL Pro
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    1,135
    Bump because of two rather important edits.

  7. #7
    Intern Contributor
    Join Date
    Sep 2012
    Posts
    85
    Hi Thokra:
    Really appreciate if you took a look. I am working in Win7/Visual C++ 2010. The code compiles ok but I just get a blank screen. The triangle is not drawn.
    Thanks,
    Sam

    Main program
    Code :
    ////////////////////////////////////////////////////          
    // trianglePipeline.cpp
    ////////////////////////////////////////////////////
     
    #include <iostream>
    #include <fstream>
     
    #  include <GL/glew.h>
    #  include <GL/freeglut.h>
    #  include <GL/glext.h>
    #pragma comment(lib, "glew32.lib") 
     
    struct Vertex
    {
       float coords[4];
       float colors[4];
    };
     
    struct Matrix4x4
    {
       float entries[16];
    };
     
    static const Matrix4x4 IDENTITY_MATRIX4x4 = 
    {
       {
          1.0, 0.0, 0.0, 0.0,
          0.0, 1.0, 0.0, 0.0,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0
       }
    };
     
    static Matrix4x4
       MVmatrix,
       projMatrix;
     
    static unsigned int
       vShaderProgId,
       fShaderProgId,
       MVmatrixUnifLoc,
       projMatrixUnifLoc,
       pipeline,
       buffer, 
       vao;    
     
    Vertex triangleVertices[] = // Triangle vertex data: { {Coordinates 4-vector}, {Color 4-vector} } 
    {
       { { 20.0, 20.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0, 1.0 } },
       { { 80.0, 20.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0, 1.0 } },
       { { 20.0, 80.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0, 1.0 } }
    };
     
    //Function to read a text file.
    char* readTextFile(char* aTextFile)
    {
       FILE* filePointer = fopen(aTextFile, "rb");	
       char* content = NULL;
       long numVal = 0;
     
       fseek(filePointer, 0L, SEEK_END);
       numVal = ftell(filePointer);
       fseek(filePointer, 0L, SEEK_SET);
       content = (char*) malloc((numVal+1) * sizeof(char)); 
       fread(content, 1, numVal, filePointer);
       content[numVal] = '\0';
       fclose(filePointer);
       return content;
    }
     
    // Initialization routine.
    void setup(void)
    {
       glGenProgramPipelines(1, &pipeline);
     
       char* vShaderSrc = readTextFile("vShader.glsl");
       char* fShaderSrc = readTextFile("fShader.glsl");
     
       vShaderProgId = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, (const char**) &vShaderSrc);
       fShaderProgId = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, (const char**) &fShaderSrc); 
     
       glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, vShaderProgId);
       glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, fShaderProgId);
     
       glClearColor(1.0, 1.0, 1.0, 0.0);
     
       glGenVertexArrays(1, &vao);
       glBindVertexArray(vao);
     
       glGenBuffers(1, &buffer);
       glBindBuffer(GL_ARRAY_BUFFER, buffer);
       glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
     
       glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(triangleVertices[0]), 0);
       glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(triangleVertices[0]), (GLvoid*)sizeof(triangleVertices[0].coords));
     
       glEnableVertexAttribArray(0);
       glEnableVertexAttribArray(1);
     
       Matrix4x4 MVmatrix = IDENTITY_MATRIX4x4;
       MVmatrixUnifLoc = glGetUniformLocation(vShaderProgId,"MVmatrix"); 
       glProgramUniformMatrix4fv(vShaderProgId, MVmatrixUnifLoc, 1, GL_TRUE, MVmatrix.entries);
     
       Matrix4x4 projMatrix = 
       {
          {
             0.02, 0.0,  0.0, -1.0,
             0.0,  0.02, 0.0, -1.0,
             0.0,  0.0, -1.0,  0.0,
             0.0,  0.0,  0.0,  1.0
          }
       };
       projMatrixUnifLoc = glGetUniformLocation(vShaderProgId,"projMatrix"); 
       glProgramUniformMatrix4fv(vShaderProgId, projMatrixUnifLoc, 1, GL_TRUE, projMatrix.entries);
    }
     
    // Drawing routine.
    void drawScene(void)
    {
       glBindProgramPipeline(pipeline);
       glClear(GL_COLOR_BUFFER_BIT);
     
       glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
     
       glFlush();
    }
     
    // OpenGL window reshape routine.
    void resize(int w, int h)
    {
       glViewport(0, 0, w, h);
    }
     
    // Main routine.
    int main(int argc, char* argv[])
    {	
       glutInit(&argc, argv);
     
       glutInitContextVersion(4, 3);
       glutInitContextProfile(GLUT_CORE_PROFILE);
     
       glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
       glutInitWindowSize(500, 500);
       glutInitWindowPosition(100, 100); 
       glutCreateWindow("trianglePipeline");
       glutDisplayFunc(drawScene);
       glutReshapeFunc(resize);
     
       glewExperimental = GL_TRUE;
       glewInit();
     
       setup();
     
       glutMainLoop();
    }

    Vertex shader:
    Code :
    #version 430 core
     
    layout(location=0) in vec4 vertexCoordsIn;
    layout(location=1) in vec4 vertexColorsIn;
    out vec4 colorsExport;
     
    uniform mat4 MVmatrix;
    uniform mat4 projMatrix;
     
    void main(void)
    {
       gl_Position = projMat * MVmatrix * vertexCoordsIn;
       colorsExport = vertexColorsIn;
    }

    Fragment shader:
    Code :
    #version 430 core
     
    in vec4 colorsExport;
    out vec4 colorsOut;
     
    void main(void)
    {
       colorsOut = colorsExport;
    }

  8. #8
    Senior Member OpenGL Pro
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    1,135
    The API code looks good at first glance. The shaders do as well - for a monolithic program at least. You didn't redefine gl_PerVertex in the vertex shader, however. First, please check the program logs to verify that no compilation/linking error happened. I suspect it did. If redeclaration doesn't work out, I'd try it with a monolithic program to confirm the rest of the code is proper. You can redeclare gl_PerVertex for that purpose just the same. gl_Position will be all you need and matching is of no concern since there aren't subsequent pipeline stages that read any other value (other than the fixed stages which will use gl_Position).

  9. #9
    Intern Contributor
    Join Date
    Sep 2012
    Posts
    85
    Hi Thokra:
    The fragment shader and pipeline don't generate errors. The only one is from the vertex shader as below:

    Vertex info
    -----------
    0(12) : error C7592: ARB_separate_shader_objects requires built-in block gl_PerVertex to be redeclared before accesing its members

    So, I do need to redeclare gl_PerVertex. Sorry, but I have to ask: how does one do this exactly? If you could tell me what the lines are I have to include in my vertex and fragment shaders it would really help.

    Thanks again,
    Sam

  10. #10
    Senior Member OpenGL Pro
    Join Date
    Apr 2010
    Location
    Germany
    Posts
    1,135
    Code :
    // in the vertex shader
    out gl_PerVertex
    {
      vec4 gl_Position;
      // you don't need to redeclare gl_PointSize and gl_ClipDistance[] if you don't use write to them
    };

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •