Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing a glProgram among multiple OpenGL-ES contexts?

I have an iPad app with multiple OpenGL-ES views that use OpenGL ES 2.0 and a custom fragment shader. Currently the shaders get compiled and linked for each individual OpenGL view. I'd like to compile and link the shaders once, and re-use them for each view. In theory this should just be a matter of calling

gluseProgram(gProgramHandle);

in my render() method, prior to rendering, and loading gProgramHandle once, right? But this doesn't work. When I switch to using a single gProgramHandle (set to -1 at initialization), only one OpenGL view works, and the others show up as a dark green rectangle. What am I doing wrong?

- (void)loadShaders
{
   if (gProgramHandle == -1)
{
    NSLog(@"Compiling shaders...");

    GLuint vertexShader = [self compileShader:@"AIMGsiVertexShader" withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:@"AIMGsiFragmentShader" withType:GL_FRAGMENT_SHADER];

    gProgramHandle = glCreateProgram();
    glAttachShader(gProgramHandle, vertexShader);
    glAttachShader(gProgramHandle, fragmentShader);
    glLinkProgram(gProgramHandle);

    GLint linkSuccess;
    glGetProgramiv(gProgramHandle, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE)
    {
            // If there was an error when compiling the gsls shaders, report the compile error and quit.
        GLchar messages[256];
        glGetProgramInfoLog(gProgramHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }

    NSLog(@"Done compiling and linking shaders.");
}

    // We can efficiently switch between shaders by calling glUseProgram() to use the program with the shaders we want to use.
glUseProgram(gProgramHandle);

    // Gradient map values are sent in this vector: numValuesInCache, factor, offset
_gradientValsUniform = glGetUniformLocation(gProgramHandle, "GradientVals");
_numColorsInGradient = glGetUniformLocation(gProgramHandle, "NumColorsInCache");
_gradientColorsArray = glGetUniformLocation(gProgramHandle, "ColorsArray");


_positionSlot = glGetAttribLocation(gProgramHandle, "Position");

glEnableVertexAttribArray(_positionSlot);

_projectionUniform = glGetUniformLocation(gProgramHandle, "Projection");
_modelViewUniform = glGetUniformLocation(gProgramHandle, "Modelview");

_texCoordSlot = glGetAttribLocation(gProgramHandle, "TexCoordIn");
glEnableVertexAttribArray(_texCoordSlot);
_textureUniform = glGetUniformLocation(gProgramHandle, "Texture");

}
like image 514
Vern Jensen Avatar asked Nov 26 '12 22:11

Vern Jensen


2 Answers

Here is quote from EAGLSharegroup Class Reference about sharing:

Currently, the sharegroup manages textures, buffers, framebuffers, and renderbuffers.

As you see Shaders/Programs/Pipelines are not mentioned.

However here is quote from Apple dev forum:

It is legal to share Shaders/Programs/Pipelines, but actually using the same Program at the same time is something to be avoided.

The problem is that Uniform values are properties of the Program itself, and so if you're using the same program from multiple threads that will violate the rules against modifying an object from one thread while using it from another.

If the usage does not overlap (one context produces programs, another renders, for example), that should work out fine.

To test sharing of shaders and pipelines I developed simple project for iPad: https://github.com/Gubarev/shared-shaders-test. And I can confirm that sharing works!

like image 183
Vlad Avatar answered Oct 08 '22 14:10

Vlad


You can't share a program across contexts. A GL context is a collection of state information and the program is part of that state. I would question whether every view needs its own context, this may well not be needed. If you do all your rendering on the main thread then share the context between the views and set up the state you need in each draw call for each view. If you are rendering on multiple threads look at creating contexts with [[EAGLContext alloc] initWithAPI:... sharegroup:...] and pass in the sharegroup from your main context.

like image 21
combinatorial Avatar answered Oct 08 '22 14:10

combinatorial