OpenGL: Create proxy user for shader

64 Views Asked by At

Prior to the OpenGL-pipeline: I want to use a special vertex shader for some objects I render. So I thought of this:

int currProgram = glGetInteger(GL_CURRENT_PROGRAM);
int currVertexShader = 0;
if (currProgram == 0) {
    glUseProgram(programName);
} else {
    currVertexShader = GLStatics.getShader(currProgram,
            GL_VERTEX_SHADER);
    if (currVertexShader != 0) {
        glDetachShader(currProgram, currVertexShader); // <-- problem here
    }
    glAttachShader(currProgram, shaderName);
    GLStatics.linkProgramSafe(currProgram);
}
// Actual render code
if (currProgram != 0) {
    glDetachShader(currProgram, shaderName); // Can safely detach
    if (currVertexShader != 0) {
        glAttachShader(currProgram, currVertexShader);
    }
    GLStatics.linkProgramSafe(currProgram);
}
glUseProgram(currProgram);

So I have to static GLObjects: shaderName, which is the compiled vertex shader I want to use and programName which is the program I bind if no other program is bound beforehands.

I thought this would run fine when really there is a problem. When glDeleteShader() was called on the vertex shader of the currently bound program before executing the code the shader object gets deleted (in the marked line) and can't be reattached afterwards.

Is there an easy way to fix this issue (easy in the sense of efficient)?

For the sake of completeness the GLStatics class:

public class GLStatics {
    public static ByteBuffer createDirectBuffer(int size) {
        return ByteBuffer.allocateDirect(size);
    }

    public static int createProgramSafe() {
        int programName = glCreateProgram();
        if (programName == 0) {
            throw new IllegalStateException(
                    "GL Error: Created Program is 0. Can't proceed.");
        }
        return programName;
    }

    public static int getShader(int program, int searchedType) {
        int shaderCount = glGetProgrami(program, GL_ATTACHED_SHADERS);
        IntBuffer attachedShaders = createDirectBuffer(shaderCount * 4)
                .asIntBuffer();
        IntBuffer count = createDirectBuffer(4).asIntBuffer();
        glGetAttachedShaders(program, count, attachedShaders);
        assert count.get() == shaderCount;
        for (int i = 0; i < shaderCount; ++i) {
            int shaderCandidate = attachedShaders.get();
            if (searchedType == glGetShaderi(shaderCandidate, GL_SHADER_TYPE)) {
                return shaderCandidate;
            }
        }
        return 0;
    }

    public static void linkProgramSafe(int program) {
        glLinkProgram(program);
        if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) {
            int errorLength = glGetProgrami(program, GL_INFO_LOG_LENGTH);
            String error = glGetProgramInfoLog(program, errorLength);
            throw new IllegalStateException(error);
        }

    }
}
1

There are 1 best solutions below

2
On BEST ANSWER

You could have another shader program that you use just for "parking" the shader while it's detached from the shader you're operating on. That will maintain a reference that keeps it from getting deleted for good.

Just showing the extended parts of the code, with dummyProgram being a program you created solely for this purpose:

if (currVertexShader != 0) {
    glAttachShader(dummyProgram, currVertexShader);
    glDetachShader(currProgram, currVertexShader);
}
...
if (currVertexShader != 0) {
    glAttachShader(currProgram, currVertexShader);
    glDetachShader(dummyProgram, currVertexShader);
}

This will work based on the following definition in the spec (appendix D.1.2 in OpenGL 3.3 spec):

When a shader object or program object is deleted, it is flagged for deletion, but its name remains valid until the underlying object can be deleted because it is no longer in use. A shader object is in use while it is attached to any program object.

So simply attaching the shader to any program is enough for it being considered "in use", and preventing it from being deleted. If you're interested in more details about the subtle aspects of program/shader lifetimes, this is an answer I wrote to an earlier question: glDeleteShader - is the order irrelevant?.