I'm trying to create a single compute a shader program computeProgram and attach two source codes on it. Here are my codes:
unsigned int computeProgram = glCreateProgram();
glAttachShader(computeProgram, MyFirstComputeShaderSourceCode);
glAttachShader(computeProgram, MySecondComputeShaderSourceCode);
glLinkProgram(computeProgram);
glGetProgramiv(computeProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(computeProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::COMPUTE_PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
exit(1);
}
I get this type of linking error information:
ERROR::SHADER::COMPUTE_PROGRAM::LINKING_FAILED
ERROR: Duplicate function definitions for "main"; prototype: "main()" found.
I do have main functions in both shader source codes, and I understand why this is not gonna work cause there is only one main function expected in one program. But here comes my question: If I'm trying to link a vertex shader source and a fragment shader source to a single program, say, renderProgram, there are also two main functions, one in vertex shader, one in fragment shader. However, if I link these two, it somehow works fine.
Why is this difference happen? And if I want to use these two compute shaders, am I supposed to create two compute programs in order to avoid duplication of the main function?
Any help is appreciated!!
Why is this difference happen?
When you link a vertex shader and a fragment shader to the same shader program, then those two (as their names imply) are in different shader stages. Every shader stage expects exactly one definition of the main() function.
When you attach two shaders that are in the same shader stage, such as your two compute shader objects, then those get linked into the same shader stage (compute). And that does not work.
And if I want to use these two compute shaders, am I supposed to create two compute programs in order to avoid duplication of the main function?
Yes. When you have two compute shaders that each define their own functionality in terms of a main() function, then creating two shader programs each with one of the shader objects linked to it would work. Especially, when your two shaders have completely different interfaces with the host, such as SSBOs or samplers/images.
Related
I have some math functions written in GLSL and I want to use them in TES, geometry and fragment stages of the same shader program. All of them are pretty valid for all these shader types, and for now the code is just copy-pasted across shader files.
I want to extract the functions from the shader files and put them into a separate file, yielding a shader "library". I can see at least two ways to do it:
Make a shader source preprocessor which will insert "library" code into shader source where appropriate.
Create additional shader objects, one per shader type, which are compiled from the library source code, and link them together. This way the "library" executable code will be duplicated across shader stages on compiler and linker level. Actually, this is how "library" shaders may be used, but in this variant they are stage-specific and cannot be shared across pipeline stages.
Is it possible to compile shader source only once (with appropriate restrictions), link it to a shader program and use it in any stage of the pipeline? I mean something like this:
GLuint shaderLib = glCreateShader(GL_LIBRARY_SHADER);
//...add source and compile....
glAttachShader(shProg, vertexShader);
glAttachShader(shProg, tesShader);
glAttachShader(shProg, geomShader);
glAttachShader(shProg, fragShader);
glAttachShader(shProg, shaderLib);
glLinkProgram(shProg); // Links OK; vertex, TES, geom and frag shader successfully use functions from shaderLib.
Of course, library shader should not have in or out global variables, but it may use uniforms. Also, function prototypes should be declared before usage in each shader source, as it is possible to do when linking several shaders of the same type into one program.
And if the above is not possible, then WHY? Such "library" shaders look very logical for the C-like compilation model of GLSL.
Is it possible to compile shader source only once (with appropriate restrictions), link it to a shader program and use it in any stage of the pipeline?
No. I'd suggest, if you have to do this, to just add the text to the various shaders. Well, don't add it directly to the actual string; instead, add it to the list of shader strings you provide via glShaderSource/glCreateShaderProgram.
And if the above is not possible, then WHY?
Because each shader stage is separate.
It should be noted that not even Vulkan changes this. Well, not the way you want. It does allows you to have the reverse: multiple shader stages all in a single SPIR-V module. But it doesn't (at the API level) allow you to have multiple modules provide code for a single stage.
All the high level constructs of structs, functions, linking of modules, etc, are mostly all just niceties that the APIs provide to make your life easier on input. HLSL for example allows you to do use #include's, but to do something similar in GLSL you need to perform that pre-process step yourself.
When the GPU has to execute your shader it is running the same code hundreds, thousands, if not millions of times per frame - the driver will have converted your shader into its own HW specific instruction set and optimised it down to the smallest number of instructions practicable to guarantee best performance of their instruction caches and shader engines.
http://docs.gl/gl4/glCreateShaderProgram
Pseudo code:
const GLuint shader = glCreateShader(type);
if (shader) {
glShaderSource(shader, count, strings, NULL);
glCompileShader(shader);
const GLuint program = glCreateProgram();
if (program) {
GLint compiled = GL_FALSE;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
if (compiled) {
glAttachShader(program, shader);
glLinkProgram(program);
glDetachShader(program, shader);
}
/* append-shader-info-log-to-program-info-log */
}
glDeleteShader(shader);
return program;
}
else {
return 0;
}
I didn't know that it is possible to compile several shaders at once. The problem that I have is that the documentation doesn't tell me how to call this.
What type should I specify if I want to compile a vertex + fragment shader? Initially I tried glCreateShader(GL_VERTEX_SHADER | GL_FRAGMENT_SHADER); but then it complained about two main functions. OpenGL probably interpreted two shaders as one shader.
glCreateShaderProgram() only compiles and links a single shader. The last argument is an array of strings just like the corresponding argument for glShaderSource(), which is also used for specifying the source of one shader. Being able to pass multiple strings is just a convenience for both of those calls. For example, if you have your shader code as an array of strings (say one per line), you can directly pass them in, without a need to concatenate them first.
glCreateShaderProgram() is intended to be used in conjunction with program pipeline objects. This mechanism allows you to combine shaders from several programs, without a need to link them. A typical call sequence would look like this:
GLuint vertProgId = glCreateShaderProgram(GL_VERTEX_SHADER, 1, &vertSrc);
GLuint fragProgId = glCreateShaderProgram(GL_FRAGMENT_SHADER, 1, &fragSrc);
GLuint pipelineId = 0;
glGenProgramPipelines(1, &pipelineId);
glUseProgramStages(pipelineId, GL_VERTEX_SHADER_BIT, vertProgId);
glUseProgramStages(pipelineId, GL_FRAGMENT_SHADER_BIT, fragProgId);
glBindProgramPipeline(pipelineId);
From the linked documentation:
glCreateShaderProgram creates a program object containing compiled and linked shaders for a single stage specified by type.
[emphasis mine]
What it means is that you cannot use it with a list of shaders targeting different parts of the rendering pipeline.
However, you could perhaps modify the sample implementation to cover that need for you. It would require to change the function signature by replacing the first parameter by an array of GLenum of count elements, then turn the implementation into a loop on each pair shader[i], strings[i] to add them to the generated program, where shader is an array of shader ids associated with each source string separately.
Once each shader has been compiled, the code would then link the whole into a program, release all the shader ids, and return the program id (actual implementation left as an exercise).
I don't think it's possible to create multiple shaders at once. glCreateShaderProgram just does everything needed to create a shader for you (create, compile, link).
If you look at the documentation at opengl.org, you will see that you can only create one shader at a time. You can only specify one of the following GLenums: GL_COMPUTE_SHADER, GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, or GL_FRAGMENT_SHADER.
I think you are right about what you are thinking. OpenGL is probably interpreting two shaders as one and that is not possible.
You can do this:
glCreateShader(GL_VERTEX_SHADER);
glCreateShader(GL_FRAGMENT_SHADER);
Look at the Description field of the source I have provided. It will explain what further to do with the shader objects.
I am not sure why you can pass an array of strings. It could be if you had multiple small snippets of shader code in different source code files.
Source: https://www.opengl.org/sdk/docs/man/html/glCreateShader.xhtml
What is the easiest way to join more shaders (sources) to a glsl program? Normally when a vertex shader and fragment shader are attached to a program does something like this:
vertex = glCreateShader(GL_VERTEX_SHADER);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
programa = glCreateProgram();
char *vsFuente = LEE_SHADER(rutaVert);
char *fsFuente = LEE_SHADER(rutaFrag);
if(vsFuente == NULL)return GL_FALSE;
if(fsFuente == NULL)return GL_FALSE;
const char *vs = vsFuente;
const char *fs = fsFuente;
glShaderSource(vertex ,1,&vs,NULL);
glShaderSource(fragment,1,&fs,NULL);
delete [] vsFuente;
delete [] fsFuente;
glCompileShader(vertex);
glCompileShader(fragment);
glAttachShader(programa,vertex);
glAttachShader(programa,fragment);
glLinkProgram(programa);
Well, if I want to join another pair of shader (vertex and fragment) to the same program, how do I do it? I use version 2.0 of OpenGL
Exactly the same way you added the shaders you already have in your code. You create more shaders, call glCompileShader() and glAttachShader() for them, and then call glLinkProgram().
You need to be aware of what this is doing, though. While it is perfectly legal to add multiple shaders of the same type to a program, only one of the shaders of each type can have a main() function.
The typical setup I have seen when multiple shaders of the same type are attached to a program (which is not very common) is that some of the shaders contain collections of "utility" functions, and then there's one shader of each type type that contains the main() function, and implements the overall shader logic. This setup can be useful for complex shaders, since the shaders with the utility functions can be compiled once, and then attached to multiple different programs without having to compile them each time.
I'm not sure if this is what you're after. If you really just want to use different sets of shaders, the much more common setup is that you create a program for each pair of vertex/fragment shaders, and then switch between them during your rendering by calling glUseProgram() with the program you need for each part of the rendering.
I recently read that you can
"have multiple instances of OpenGL shaders"
but no other details were given on this.
I'd like some clarification as to what exactly this means.
For one, I know you can have more than one glProgram running, and that you can switch between them. Is that all this is referring to? I assuming that switching between several created shader programs per frame would essentially mean I am using several programs "simultaneously".
Or does it somehow refer to having multiple "instances" of the same shader program? That would make no sense to me.
Some basic clarification would be enjoyed here!
When you create a program object you're linking together several shaders. Usually at least a vertex and a fragment shader. Now say you want to render, say some glow around some object. That glow would be created by a different fragment shader, but the vertex shader would be the same as for the regular appearance. Now to save resources you can use the same vertex shader in multiple programs but with different fragment shaders being linked in. Of course you could also have the same fragment shader and different vertex shaders.
In short you can link a single shader into an arbitrary number of programs. As long as the linked shader stages are compatible with each other this helps with modularization.
I would like to have two pixel shaders; the first doing one thing, and then the next doing something else. Is this possible, or do I have to pack everything into the one shader?
You can do do this, e.g. by doing function calls from the main entrypoint to functions that are implemented in the various shader objects.
main() {
callToShaderObject1()
callToShaderObject2()
}
each of those callToShaderObject functions can live in different shader objects, but all objects have to be attached and linked in the same program before it can be used.
They can't run at the same time, but you are free to use different shaders for different geometry, or to render in multiple passes using different shaders.
The answer depends on the GL version, you need to check glAttachShader documentation for the version you are using. GLES versions (including webgl) do not allow attaching multiple fragment shaders to a single program and will raise GL_INVALID_OPERATION error when attempted.
glAttachShader - OpenGL 4:
It is permissible to attach multiple shader objects of the same type because each may contain a portion of the complete shader.
glAttachShader - OpenGL ES 3.2:
It is not permissible to attach multiple shader objects of the same type.