Type Qualifier (GLSL)
A type qualifier is used in the OpenGL Shading Language (GLSL) to modify the storage or behavior of global and locally defined variables. These qualifiers change particular aspects of the variable, such as where they get their data from and so forth. They come in a number of different categories.
Storage qualifiers define where a variable's storage comes from.
If no storage qualifier is specified when declaring a global variable, then the variable is just a normal global variable. All global variables have a scope of the particular shader stage. So if you have two vertex shader objects that define int someValue;, when you link them into a program, they will both be referring to the same value.
If no storage qualifier is specified for a local variable, then the variable is a local variable that can be modified.
Global or local variables can be declared with the const qualifier. This means that the variable's value cannot be changed after it is initialized. This also means that the variable declaration must initialize the variable.
const variables are considered constant expressions. The initializer for a const variable must itself be a constant expression. Constant expressions are expressions that only contain:
- Literal values (3, 2.4, etc)
- A previously defined global or local variable that is qualified as const.
- A type constructor that is given only constant expressions as arguments.
- A operator whose arguments are constant expressions (ie: 5+4).
- A built-in function call whose arguments are all constant expressions. Notably exempt from this are the texture functions, because uniform variables are not compile-time constant. All of the texture functions take a sampler uniform as a parameter, and therefore they are not compile-time constants.
Shader stage inputs and outputs
Global variables declared with the in qualifier are shader stage input variables. These variables are given values by the previous stage (possibly via interpolation of values output from multiple shader executions). These variables are not constant (in the sense of const), but they cannot be changed by user code.
Global variables declared with the out qualifier are shader stage output variables. These values are passed to the next stage of the pipeline (possibly via interpolation of values output from multiple shader executions). The shader must set all output variables at some point in its execution (unless the fragment shader executes the discard statement), except for those outputs that are not read by the next shader.
Neither qualifier can be used on local variables.
These variables can be of any non-sampler basic type. They cannot be of struct types, but they can be arrays. There are usually very strict limits on the number of inputs and outputs available to a shader stage.
These variables are how the shader communicates to the earlier and later parts of the pipeline. Each variable name and type will match with an equivalent variable name and type on the next or previous shader stage. So, for each vertex shader output, the fragment shader will have a matching input with the same name and type. Using the same name with a different type is a linker error.
Also, it is allowed to have output variables that are not read by the next stage. It is not allowed to have input variables not written by the previous stage.
There are some special cases with input and output variables for different stages.
Vertex shader inputs
Inputs for vertex shaders are attributes. They are passed from vertex arrays to the vertex shader.
Array inputs can be defined. Each array value takes up one attribute index, sequentially from the first. So this input: in float values will take up 5 attributes.
Matrix inputs take up one attribute index for each row.
Tessellation control shader outputs
Tessellation Control Shader outputs can be either per-vertex or per-patch. Per-vertex outputs are aggregated into arrays, where the length of the arrays is the number of output vertices in the patch. Per-patch outputs (declared with the patch keyword) are not arrays (you can declare arrays of them, but they won't be indexed per-vertex).
TCS's can read from any per-vertex or per-patch output, so each TCS invocation can read data written by other TCS invocations (assuming you take steps to ensure that the other invocations have written to them). But each TCS invocation can only write to per-vertex outputs that are for their particular vertex. This means that the only expression in the subscript that can be when writing to a per-vertex output is gl_InvocationID.
Tessellation evaluation shader inputs
Tessellation Evaluation Shader inputs are like TCS outputs: either per-vertex or per-patch (declared with the patch keyword. Any TES invocation can read any per-vertex or per-patch input.
Geometry shader inputs
Geometry Shader inputs are aggregated into arrays, one per vertex. The length of the array depends on the input primitive type used by the GS. Each array index represents a single vertex in the input primitive.
Fragment shader outputs
Fragment shader outputs cannot be matrices or booleans; they must be floating-point or integer vectors or scalars.
Each fragment shader output corresponds to a draw buffer, set with the glDrawBuffers command. The association between output values and draw buffers is made with glBindFragDataLocation or with explicit binding via layout(location = #) in the shader before linking the program. Any unassigned outputs will be assigned values by the linker arbitrarily.
Certain inputs and outputs can have interpolation qualifiers. These are for any values which could be interpolated as a result of rasterization. These include:
- Vertex shader outputs
- Tessellation control shader inputs (to match with outputs from the VS)
- Tessellation evaluation shader outputs
- Geometry shader inputs and outputs (to match with outputs from the TES/VS)
- Fragment shader inputs
Interpolation qualifiers control how interpolation of values happens across a triangle or other primitive. There are three basic interpolation qualifiers.
- The value will not be interpolated. The value given to the fragment shader is the value from the Provoking Vertex for that primitive.
- The value will be linearly interpolated in window-space. This is usually not what you want, but it can have its uses.
- The value will be interpolated in a perspective-correct fashion. This is the default if no qualifier is present.
The centroid and sample (the latter requires GL 4.0/ARB_gpu_shader5) qualifiers affect interpolation, but they are somewhat special, grammatically speaking. Before GL 4.2/ARB_shading_language_420pack, these were considered part of the in/out storage qualifier. So their use must always be together: centroid in or sample out; no qualifiers can come between them. In 4.2 and above, they are considered axillary qualifiers, so they can be separated from the in/out storage qualifier.
They may not technically be interpolation qualifiers, but they do control aspects of interpolation. They only have an effect when Multisampling is being used.
During multisampling, if centroid is not present, then the written value can be interpolated to to an arbitrary position within the pixel. This may be the pixel's center, one of the sample locations within the pixel, or an arbitrary location. Most importantly of all, this sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. If the implementation computes the sample based on the center of the pixel, and the primitive doesn't actually cover the pixel's center (remember: in multisampling, this can still produce a non-zero number of samples), then the interpolated value will be outside of the primitive's borders.
The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area. This is useful for parameters or computations that would have undefined values if they fell outside of the primitive's area. A square root is only defined for positive numbers, so if you are taking the square root of an interpolated value, you may need to use centroid interpolation.
You should only use centroid if there is a real problem like this. In many cases interpolating without centroid doesn't pose a problem.
The sample qualifier forces OpenGL to interpolate this qualifier to the location of the particular sample. This is only really useful with per-sample shading.
Global variables and Interface Blocks can be declared with the uniform qualifier. This means that the value does not change between multiple executions of a shader during the rendering of a primitive (ie: during a glDraw* call). These values are set by the user from the OpenGL API.
They are constant, but not compile-time constant (so not const).
In OpenGL 4.3 (or with the ARB_shader_storage_buffer_object extension), interface blocks can be qualified with the buffer qualifier. This means that the storage for the block comes from a buffer object, similarly to Uniform Buffer Objects and uniform blocks. Unlike UBOs, storage blocks can be written to. They can also have an unbounded size.
Inputs, outputs, uniforms, and buffer variables for various stages can grouped into interface blocks.
Variables declared with the shared qualifier are shared among several shader invocations. Such variables can only be used in Compute Shaders. Shared variables are shared among all invocations in a work group.
There are a large number of layout qualifiers which can be applied to a variety of defined constructs, from interface blocks to shader stage inputs and outputs. These affect the storage location of their data and many other properties about where the variable's data comes from or other user-facing interfaces about the data.
There are three precision qualifiers: highp, mediump, and lowp. They have no semantic meaning or functional effect. They can apply to any floating-point type (vector or matrix), or any integer type.
All variables of a certain type can be declared to have a precision by using the precision statement. It's syntax is as follows:
precision precision-qualifier type;
In this case, type can only be float or int. This will affect all collections of that basic type. So float will affect all floating-point scalars, vectors, and matrices. The int type will affect all signed and unsigned integer scalars and vectors.
|Core in version||4.4|
|Core since version||4.2|
|Core ARB extension||ARB_shader_image_load_store|
image variables, buffer variables, and shader storage blocks (and their contents) can have a number of memory qualifiers applied to them. These affect how they can be used and how values read from/written to them will be seen in other shader invocations. Multiple qualifiers can be used on the same variable.
- Normally, the compiler is free to assume that this shader invocation is the only invocation that modifies values read through this variable. It also can freely assume that other shader invocations may not see values written through this variable.
- Using this qualifier is required to allow dependent shader invocations to communicate with one another, as it enforces the coherency of memory accesses. Using this requires the appropriate memory barriers to be executed, so that visibility can be achieved.
- When communicating between shader invocations for different rendering commands, glMemoryBarrier should be used instead of this qualifier.
- The compiler normally is free to assume that values accessed through variables will only change after memory barriers or other synchronization. With this qualifier, the compiler assumes that the contents of the storage represented by the variable could be changed at any time.
- Normally, the compiler must assume that you could access the same image/buffer object separate variables in the same shader. Therefore, if you write to one variable, and read from a second, the compiler assumes that it is possible that you could be reading the value you just wrote. With this qualifier, you are telling the compiler that this particular variable is the only variable that can modify the memory visible through that variable within this shader invocation (other shader stages don't count here). This allows the compiler to optimize reads/writes better.
- You should use this wherever possible.
- Normally, the compiler allows you to read and write from variables as you wish. If you use this, the variable can only be used for reading operations.
- Normally, the compiler allows you to read and write from variables as you wish. If you use this, the variable can only be used for writing operations (atomic writes are forbidden because they also count as reads).
These qualifiers can be used on parameters of image types. If you pass an image to a function, the function parameter's qualifier list much match that of the image declaration. The parameter could have more qualifiers, but it cannot have fewer.
There is a way to qualify certain output variables as being invariant. This allows different programs to compute the exact same answer, assuming certain conditions are met.
The invariant qualifier can be applied to an existing declaration, as in this case:
invariant gl_Position; //Not redeclared; just uses invariant. in vec3 Color; invariant Color; //Again makes existing declaration invariant.
Or you can use it at the declaration site.
invariant in vec3 Color;
|TODO: This section needs to be filled in.|
Unless you are in GLSL 4.20 or using ARB_shading_language_420pack, qualifiers always come in a particular order. For non-parameter values, the order is always this:
invariant-qualifier interpolation-qualifier layout-qualifier other-storage-qualifier precision-qualifier
The centroid qualifier, if present, must immediately precede in or out. For the purpose of ordering, it is considered part of the in or out storage qualifier, not part of the interpolation qualifier.
GLSL 4.20/ARB_shading_language_420pack removes the ordering restriction in most cases. centroid still has to immediate precede in or out. It also allows multiple layout qualifiers, but you can still only use one qualifier from most other groups (there can be multiple memory qualifiers). Also, all qualifiers must still come before the type specifier. The groups of qualifiers match the main headings above: storage, layout, precision, etc.
The GLSL defines a number of predefined variables at the various shader stages. These pre-defined variables are defined with a particular set of qualifiers, as stated in the above article. If you wish to use pre-defined variables with a different qualifier, you can re-declare the variable, but the re-declaration must use the same type. Some variables cannot be redeclared with a new qualifier. For example: gl_Position in the vertex shader cannot use an interpolation qualifier at all.
The following qualifiers are deprecated as of GLSL 1.30 (OpenGL 3.0) and removed from GLSL 1.40 and above.
The attribute qualifier is effectively equivalent to an input qualifier in vertex shaders. It cannot be used in any other shader stage. It cannot be used in interface blocks.
The varying qualifier is equivalent to the input of a fragment shader or the output of a vertex shader. It cannot be used in any other shader stages. It cannot be used in interface blocks.