Shader Compilation

From OpenGL Wiki
Jump to navigation Jump to search

Shader Compilation is the process of text in the OpenGL Shading Language and loading it into OpenGL to be used as a Shader. OpenGL has three ways to compile shader text into useable OpenGL objects. All of these forms of compilation produce a Program Object.

Shader and program objects

A Program Object can contain the executable code for all of the Shader stages, such that all that is needed to render is to bind one program object. Building programs that contain multiple shader stages requires a two-stage compilation process.

This two-stage compilation process mirrors the standard compile/link setup for C and C++ source code. C/C++ text is first fed through a compiler, thus producing an object file. To get the executable code, one or more object files must be linked together.

With this method of program creation, shader text is first fed through a compiler, thus producing a shader object. To get the executable program object, one or more shader objects must be linked together.

Shader object compilation

The first step is to create shader objects for each shader that you intend to use and compile them. To create a shader object, you call this function:

GLuint glCreateShader(GLenum shaderType​);

This creates an empty shader object for the shader stage given by given shaderType​. The shader type must be one of GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADER, or GL_COMPUTE_SHADER. Note that the control and evaluation shaders require GL 4.0 (or ARB_tessellation_shader), and the compute shader requires GL 4.3 (or ARB_compute_shader).

Once you have a shader object, you will need to give it the actual text string representing the GLSL source code. That is done via this function:

void glShaderSource(GLuint shader​, GLsizei count​, const GLchar **string​, const GLint *length​);

This function takes the array of strings, given by string​ and stores it into shader​. Any previously stored strings are removed. count​ is the number of individual strings. OpenGL will copy these strings into internal memory.

When the shader is compiled, it will be compiled as if all of the given strings were concatenated end-to-end. This makes it easy for the user to load most of a shader from a file, but to have a standardized preamble that is prepended to some group of shaders.

The length​ can be either NULL or an array of count​ integers. These are the lengths of the corresponding strings in the string​ array. This allows you to use non-NULL-terminated strings. If you pass NULL, then OpenGL will assume all of the strings are NULL-terminated and will therefore compute the length in the usual way.

Once shader strings have been set into a shader object, it can be compiled with this function:

void glCompileShader(GLuint shader​);

It compiles the given shader.

Shader error handling

Compilation may or may not succeed. Shader compilation failure is not an OpenGL Error; you need to check for it specifically. This is done with a particular call to glGetShaderiv:

GLint success = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

If success​ is GL_FALSE, then the most recent compilation failed. Otherwise, it succeeded.

Shader compilation is pass/fail, but it is often useful to know why. This, like in most languages, is provided as text messages. OpenGL allows you to query a log containing this information. First, you must use glGetShaderiv to query the log's length:

GLint logSize = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);

This tells you how many bytes to allocate; the length includes the NULL terminator. Once you have the length and have allocated sufficient memory, you can use this function to get the log:

void glGetShaderInfoLog(GLuint shader​, GLsizei maxLength​, GLsizei *length​, GLchar *infoLog​);

maxLength​ is the size of infoLog​; this tells OpenGL how many bytes at maximum it will write into infoLog​. length​ is a return value, specifying how many bytes it actually wrote into infoLog​; you may pass NULL if you don't care.

Shader compilation error checking.

GLuint shader = glCreateShader(...);

// Get strings for glShaderSource.
glShaderSource(shader, ...);

glCompileShader(shader);

GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
	GLint maxLength = 0;
	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);

	// The maxLength includes the NULL character
	std::vector<GLchar> errorLog(maxLength);
	glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);

	// Provide the infolog in whatever manor you deem best.
	// Exit with failure.
	glDeleteShader(shader); // Don't leak the shader.
	return;
}

// Shader compilation is successful.


Program linking

One you have successfully compiled the shader objects of interest, you can link them into a program. This begins by creating a program object via this command:


Cleanup

After linking (whether successfully or not), it is a good idea to detach all shader objects from the program. This is done via this function:


If you do not intend to use this particular shader object in the linking of another program, you may delete it. This is done via glDeleteShader. Note that the deletion of a shader is deferred until the shader object is no longer attached to a program. Therefore, it is a good idea to detach shaders after linking.

Separate programs

Separate Shader Stages
Core in version 4.6
Core since version 4.1
Core ARB extension ARB_separate_shader_objects


Program pipelines

Binary upload

Program Binary
Core in version 4.6
Core since version 4.1
Core ARB extension ARB_get_program_binary


Error handling

Interface matching