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);
}
}
}
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:This will work based on the following definition in the spec (appendix D.1.2 in OpenGL 3.3 spec):
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?.