Problems re-attaching a shader to a program object

I want to detach then reattach a different shader object to a program object.

I create three empty shader objects with glCreateShader.
Provide source code for these shaders with glShaderSource.
Compile each of the shaders with glCompileShader.
Create a program object with glCreateProgram.
Attach all the shader objects to the program object with glAttachShader.
Link the program object with glLinkProgram.
Install the executable program as part of OpenGL’s current state with glUseProgram.
Set the uniform variables by querying the locations of these variables with glGetUniformLocation and then set their values with glUniform.

This works great but now I want to simply use a different fragment shader within the program.
I detach the old fragment shader using glDetachShader.
Recompile the new source using glShaderSource and glCompileShader.
Reattach it to the program object with glAttachShader

Then the only way I can get it to work is to re-link the program using glLinkProgram.
The problem I have is that the uniform variables seem to have gone in the other shader objects which the new shader needs.

Any ideas what I am doing wrong?

Thanks again.
Edit: What it seems that I have to do is just create another program with the different fragment shader in it. Then I should be able to switch between the two. Does this mean that glDetachShader can only be called before glLinkProgram?

The glslang compiling/linking system is based on C/C++ compiling and linking, so we’ll use that as an analogy.

So you have 3 .cpp files (A.cpp, B.cpp, and C.cpp). These each compile to produce 3 separate object files (A.o, B.o, and C.o). You then use the linker to link these 3 object files together to produce Prog1.exe.

So, let is look at what the glslang version of this looks like. It looks like (in pseudocode):


A_obj = glCreateShader();
glShaderSource(A_obj, A_cpp);
glCompileShader(A_obj);
B_obj = glCreateShader();
glShaderSource(B_obj, B_cpp);
glCompileShader(B_obj);
C_obj = glCreateShader();
glShaderSource(C_obj, C_cpp);
glCompileShader(C_obj);
Prog1_exe = glCreateProgram();
glAttachShader(Prog1_exe, A_obj);
glAttachShader(Prog1_exe, B_obj);
glAttachShader(Prog1_exe, C_obj);
// Other program setup.
glLinkProgram(Prog1_exe);

This provides you with the 3 shader files linked into a single program, “Prog1_exe”.

Now, what you are trying to do is change Prog1_exe by removing one of the shader objects and adding another.

Well, let’s look at it by analogy. If you have Prog1.exe on your harddisk, can you swap out object files without re-linking it? And if you re-link it, have you not destroyed the original Prog1.exe and replaced it with a new one? And if you do so, has Prog1.exe not lost the functionality that was in the shader you removed?

So, by analogy, you can do what you’re trying to. But it is meaningless until you call “glLinkProgram”. And once you do, you have effectively overwritten the old program with this new one, and therefore you will lose functionality in any detached shaders.

Now, you can do what you’re trying to do, but you don’t actually want to do that. glLinkProgram should be considered to be a slow operation (because it most certainly is), so you should avoid doing it at any arbitrary time. It is better to create two programs and simply pick one at runtime than to be linking programs every frame.

You don’t have to attach/detach once more. Compile same shader with a different source (or same but edited in my example), then link program again?


bool program::update()
{
	std::for_each(shaders.begin(), shaders.end(), boost::bind(&shader::update, _1));
	
	if(!link())		return false;
	if(!validate())	return false;
	return true;
}

bool shader::update()
{
	if(!source(name()) || !compile())
	{
		info();
		return false;
	}

	return true;
}

Thanks for taking the time to explain that to me.
I decided to take your advice and create two programs since it will be easier and quicker to simply switch between the two.

Thanks again.

Detach can be called before or after glLinkProgram.

The problem that you have is that you are detaching a fs and attaching another fs. Then you don’t link.
And then when you discovered that you do NEED TO link, you are not getting the new uniform locations.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.