Difference between revisions of "Interface Block (GLSL)"

From OpenGL.org
Jump to: navigation, search
(Separable program node.)
m (Bot: Updating section links to use redirects.)
 
(28 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{ infobox feature
+
{{stack
 +
|{{ infobox feature
 
| core = 3.1
 
| core = 3.1
 
| core_extension = [http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt ARB_uniform_buffer_object], [http://www.opengl.org/registry/specs/ARB/shader_storage_buffer_object.txt ARB_shader_storage_buffer_object]
 
| core_extension = [http://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt ARB_uniform_buffer_object], [http://www.opengl.org/registry/specs/ARB/shader_storage_buffer_object.txt ARB_shader_storage_buffer_object]
 +
}}
 +
|{{shader float}}
 
}}
 
}}
  
An ''Interface Block'' is a group of [[GLSL]] [[Type Qualifier (GLSL)#Storage qualifiers|input, output, uniform, or storage buffer variables]]. These blocks have special syntax and semantics that can be applied to them.
+
An ''Interface Block'' is a group of [[GLSL]] [[Storage Qualifier|input, output, uniform, or storage buffer variables]]. These blocks have special syntax and semantics that can be applied to them.
  
 
== Syntax ==
 
== Syntax ==
  
Interface blocks have different meanings in their various uses, but they have the same ''syntax'' regardless of how they are used. Uniform blocks are defined as follows:
+
Interface blocks have different meanings in their various uses, but they have the same ''syntax'' regardless of how they are used. Interface blocks are defined as follows:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 21: Line 24:
 
{{param|storage_qualifier}} can be one of {{code|in}}, {{code|out}}, {{code|uniform}}, or {{code|buffer}} (the latter requires GL 4.3). This defines what kind of interface block is being created.
 
{{param|storage_qualifier}} can be one of {{code|in}}, {{code|out}}, {{code|uniform}}, or {{code|buffer}} (the latter requires GL 4.3). This defines what kind of interface block is being created.
  
{{param|block_name}} is the true name for the interface block. When the block is referenced in OpenGL code or otherwise talked about in most contexts, this is the name that is used.
+
{{param|block_name}} is the true name for the interface block. When the block is [[Program Introspection|referenced in OpenGL code]] or otherwise talked about in most contexts, this is the name that is used to refer to it. A shader cannot have multiple blocks that have the same block name ''and'' the same {{param|storage_qualifier}}.
  
{{param|instance_name}} is a GLSL name for one or more instances of the block named {{param|block_name}}. It is optional; if it is present, then all GLSL variables defined within the block must be scoped with the instance name. For example, this defines a uniform block:
+
{{param|instance_name}} is a GLSL name for one or more instances of the block named {{param|block_name}}. It is optional; if it is present, then all GLSL variables defined within the block must be qualified with the instance name. For example, this defines a uniform block:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 35: Line 38:
 
To access the {{code|projection}} member of this block, you must use {{code|matrices.projection}}.
 
To access the {{code|projection}} member of this block, you must use {{code|matrices.projection}}.
  
If it were defined as follows:
+
If it were defined as follows, without an interface name:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 45: Line 48:
 
</source>
 
</source>
  
You simply use {{code|projection}} to refer to it. So the interface name acts as a namespace qualifier. ''At no time'' can you use {{code|MatrixBlock.projection}} in GLSL.
+
You simply use {{code|projection}} to refer to it. So the interface name acts as a namespace qualifier. ''At no time'' can you use {{code|MatrixBlock.projection}} in GLSL (though this is the name that it will [[Interface Block Member Name|appear under when introspecting from OpenGL code]]).
  
 
Because these names are defined globally, they can conflict with other names:
 
Because these names are defined globally, they can conflict with other names:
Line 59: Line 62:
 
</source>
 
</source>
  
The instance name can also define an array of blocks:
+
If {{code|MatrixBlock}} had been qualified with an instance name, there would be no compiler error.
 +
 
 +
The instance name can also be used to define an array of blocks:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 71: Line 76:
 
This creates 3 separate interface blocks: {{code|matrices[0]}}, {{code|matrices[1]}}, and {{code|matrices[2]}}. These can have separate binding locations (see below), so they can come from different buffer objects.
 
This creates 3 separate interface blocks: {{code|matrices[0]}}, {{code|matrices[1]}}, and {{code|matrices[2]}}. These can have separate binding locations (see below), so they can come from different buffer objects.
  
For uniform and shader storage blocks, the array index must be a dynamically-uniform integral expression. For other kinds of blocks, they can be any arbitrary integral expression.
+
For uniform and shader storage blocks, the array index must be a [[Dynamically Uniform Expression|dynamically-uniform integral expression]]. For other kinds of blocks, they can be any arbitrary integral expression.
  
{{note|The interface name is only used by GLSL. OpenGL always uses the actual block name. Thus, the name of the above block is "{{code|MatrixBlock[1]}}".}}
+
{{note|The instance name is only used by GLSL. OpenGL always uses the actual block name. Thus, the name of the above block is "{{code|MatrixBlock[1]}}", and the names of its members are "{{code|MatrixBlock.projection}}" and so forth.}}
  
 
Linking between shader stages allows multiple shaders to use the same block. Interface blocks match with each other based on the block name and the member field definitions. So the same block in different shader stages can have ''different'' instance names.
 
Linking between shader stages allows multiple shaders to use the same block. Interface blocks match with each other based on the block name and the member field definitions. So the same block in different shader stages can have ''different'' instance names.
Line 79: Line 84:
 
=== Valid types ===
 
=== Valid types ===
  
Interface blocks cannot contain [[GLSL Types#Opaque types|opaque types]]. They also cannot contain nested structure definitions.
+
Interface blocks cannot contain [[Opaque Type|opaque types]]. They also cannot contain nested structure definitions, though block members can contain structure members. Just not definitions of new structs. So define them first, then use them within the interface.
 +
 
 +
=== Qualifiers ===
 +
 
 +
Members of interface blocks can have [[Type Qualifier (GLSL)|type qualifiers]] associated with them. Some of these may be [[Layout Qualifier (GLSL)|layout qualifiers]]. For the most part, the available type qualifiers are the set of the qualifiers that would be allowed for non-block variables of that type.
 +
 
 +
Qualifiers are applied to a block member as they normally would be for a global definition: listed before the variable name in-order:
 +
 
 +
<source lang="glsl">
 +
in BlockName
 +
{
 +
  flat ivec3 someInts; //Flat interpolation.
 +
  vec4 value;          //Default interpolation is smooth.
 +
};
 +
</source>
 +
 
 +
In some cases, members of an interface block cannot use the qualifiers allowable to global definitions. And in some cases, interface block members have additional qualifiers, typically layout qualifiers to control aspects of the variable's layout within the block.
 +
 
 +
=== Interface matching ===
 +
 
 +
Sometimes, it is important for two or more interface block definitions to match with one another. Two blocks are said to match if they define the exact same variables (type/array count and name), in the exact same order, and with the exact same variable qualifiers.
 +
 
 +
For buffer-backed storage blocks, two blocks that match can, with the proper qualifiers, refer to identical memory layouts. Furthermore, if two blocks in the same program (but different shader stages) match and have the same block name, then the program will only [[Program Introspection|advertise one such block]] to the user. This is a lot like how [[Uniform]]s with the same names and types are merged into one between stages.
 +
 
 +
For input or output qualifiers, matching block definitions are important for [[Interface Matching|interface matching between shaders in the same pipeline]]. Interface matching with interface blocks also requires that the output from the producing stage and the input from the consuming stage use the same block name. Also, the array count of the block, when declared as an array of blocks, must be correct. More details are found in the above linked page.
  
 
== Input and output ==
 
== Input and output ==
  
Input and output blocks are designed to compliment each other. Their primary utility is with [[Geometry Shader|geometry]] or [[Tessellation Shader|tessellation shaders]], as these shaders often work with aggregates of input/output values. Blocks make it easy to organize this data.
+
Input and output blocks are designed to complement each other. Their primary utility is with [[Geometry Shader|geometry]] or [[Tessellation Shader|tessellation shaders]], as these shaders often work with aggregates of input/output values. Blocks make it easy to organize this data.
  
Interface blocks can ''only'' be used to aggregate data interfaces between shaders. Vertex shaders cannot declare an input interface block, and fragment shaders cannot declare an output interface block.
+
Interface blocks for inputs/outputs can only be used to aggregate data interfaces ''between shader stages''. Vertex shaders cannot declare an input interface block, and fragment shaders cannot declare an output interface block.
  
If the input interface uses a block, then the corresponding output must also be a block that uses the same block name and members (though not necessarily the same instance name). For example, a vertex shader can pass data to a geometry shader using these block definitions:
+
If a shader stage uses an input block and it is linked directly to its previous shader stage, then that stage must provide a [[#Interface matching|matching output block, as defined above]]. For example, a vertex shader can pass data to a geometry shader using these block definitions:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 107: Line 136:
 
Notice that the geometry shader block is defined as an array in the geometry shader. It also uses a different instance name. These work perfectly fine; the GS will receive a number of vertices based on the primitive type it is designed to take.
 
Notice that the geometry shader block is defined as an array in the geometry shader. It also uses a different instance name. These work perfectly fine; the GS will receive a number of vertices based on the primitive type it is designed to take.
  
{{note| Input/output interface blocks don't use {{code|location}} names for interface matching. So in separable programs, interfaces must match directly.}}
+
{{note|Input/output interface blocks don't use [[Layout Separate Program|{{code|location}} names for interface matching]]. So in separable programs, interfaces must match directly by name.}}
  
 
The only types that are valid in input/output blocks are those which are valid as input/output variables.
 
The only types that are valid in input/output blocks are those which are valid as input/output variables.
Line 113: Line 142:
 
== Buffer backed ==
 
== Buffer backed ==
  
Uniform blocks and shader storage blocks work in very similar ways, so this section will explain the features they have in common. Collectively, these are called "buffer-backed blocks" because the storage for their contents come from a [[Buffer Object]].
+
[[Uniform Buffer Object|Uniform blocks]] and [[Shader Storage Buffer Object|shader storage blocks]] work in very similar ways, so this section will explain the features they have in common. Collectively, these are called "buffer-backed blocks" because the storage for their contents come from a [[Buffer Object]].
  
Note that shader storage blocks are a GL 4.3 feature, and thus are not available unless 4.3 or ARB_shader_storage_buffer_objects is defined.
+
Note that shader storage blocks require {{require|4.3|shader_storage_buffer_objects}}.
  
 
=== Matrix storage order ===
 
=== Matrix storage order ===
Line 157: Line 186:
 
The specific size of basic types used by members of buffer-backed blocks is defined by OpenGL. However, implementations are allowed some latitude when assigning padding ''between'' members, as well as reasonable freedom to optimize away unused members. How much freedom implementations are allowed for specific blocks can be changed.
 
The specific size of basic types used by members of buffer-backed blocks is defined by OpenGL. However, implementations are allowed some latitude when assigning padding ''between'' members, as well as reasonable freedom to optimize away unused members. How much freedom implementations are allowed for specific blocks can be changed.
  
There are four memory layout qualifiers: {{code|shared}}, {{code|packed}}, {{code|std140}}, and {{code|std420}}. Defaults can be set the same as for matrix ordering (eg: {{code|layout(packed) buffer;}} sets all shader storage buffer blocks to use {{code|packed}}). The default is {{code|shared}}.
+
There are four memory layout qualifiers: {{code|shared}}, {{code|packed}}, {{code|std140}}, and {{code|std430}}. Defaults can be set the same as for matrix ordering (eg: {{code|layout(packed) buffer;}} sets all shader storage buffer blocks to use {{code|packed}}). The default is {{code|shared}}.
  
The '''{{code|packed}}''' layout type means that the implementation determines everything about how the fields are laid out in the block. The OpenGL API can be used to query the layout for the members of a particular block. Each member of a block will have a particular byte offset, which you can use to determine how to upload its data. Also, members of a block can be optimized out if they are found by the implementation to not affect the result of the shader. Therefore, the active components of a block may not be all of the components it was defined with.
+
'''{{code|packed}}''': This layout type means that the implementation determines everything about how the fields are laid out in the block. The OpenGL API can be used to query the layout for the members of a particular block. Each member of a block will have a particular byte offset, which you can use to determine how to upload its data. Also, members of a block can be optimized out if they are found by the implementation to not affect the result of the shader. Therefore, the active components of a block may not be all of the components it was defined with.
  
The last limitation makes it difficult to share buffers between different programs, even with identical block definitions. Therefore, the '''{{code|shared}}''' type exists. It works essentially the way {{code|packed}} does; you have to query offsets for each variable. However, {{code|shared}} has two major differences. First, it guarantees that all of the variables defined in the block are considered active. Second, it guarantees that the same buffer can be used between all programs that share this same block definition, so long as:
+
'''{{code|shared}}''': This layout type works like {{code|packed}}, with two exceptions. First, it guarantees that all of the variables defined in the block are considered active; this means nothing is optimized out. Second, it guarantees that the members of the block will have the same layout as a block definition in another program, so long as:
  
* The different programs use the ''exact same'' block definition (ignoring differences in variable names).
+
* The blocks in different programs use the ''exact same'' block definition (ignoring differences in variable names and the block name itself). Be careful about specifying the [[#Matrix storage order|default matrix storage order]], as this can implicitly change the definition of blocks following them.
 
* All members of the block use explicit sizes for arrays.
 
* All members of the block use explicit sizes for arrays.
  
 
Because of these guarantees, buffer-backed blocks declared {{code|shared}} can be used with any program that defines a block with the same elements in the same order. This even works across different types of buffer-backed blocks. You can use a buffer as a uniform buffer at one point, then use it as a shader storage buffer in another. OpenGL guarantees that all of the offsets and alignments will match between two {{code|shared}} blocks that define the same members in the same order. In short, it allows the user to ''share'' buffers between multiple programs.
 
Because of these guarantees, buffer-backed blocks declared {{code|shared}} can be used with any program that defines a block with the same elements in the same order. This even works across different types of buffer-backed blocks. You can use a buffer as a uniform buffer at one point, then use it as a shader storage buffer in another. OpenGL guarantees that all of the offsets and alignments will match between two {{code|shared}} blocks that define the same members in the same order. In short, it allows the user to ''share'' buffers between multiple programs.
  
In both of the previous layout types, the user must query the offset for each variable of interest within the block. If you want to have a layout that is know without having to query this information, you may use '''{{code|std140}}''' or '''{{code|std430}}'''. The OpenGL Specification goes into great detail as to exactly how each element of these layout types are placed within a buffer. These layouts also has the same properties as '''shared''', in that all variables will be available, and that programs can share variables of this layout with each other (if the above conditions are adhered to).
+
'''{{code|std140}}''': This layout alleviates the need to query the offsets for definitions. The rules of {{code|std140}} layout explicitly state the layout arrangement of any interface block declared with this layout. This also means that such an interface block can be shared across programs, much like '''{{code|shared}}'''. The only downside to this layout type is that the rules for packing elements into arrays/structs can introduce a lot of unnecessary padding.
  
{{code|std140}} layout will generally not use the most optimal packing in terms of size. This is the only real downside to using it.
+
The rules for {{code|std140}} layout are covered quite well in the OpenGL specification ({{glref45|Section 7.6.2.2|137}}). Among the most important is the fact that arrays of types are ''not'' necessarily tightly packed. An array of {{code|float}}s in such a block will not be the equivalent to an array of {{code|floats}} in C/C++. The array stride (the bytes between array elements) is always rounded up to the size of a {{code|vec4}} (ie: 16-bytes). So arrays will only match their C/C++ definitions if the type is a multiple of 16 bytes
  
Note that {{code|std430}} layout '''cannot''' be used with uniform blocks. It can only be used with storage buffer blocks. It offers greater packing and smaller alignment requirements than {{code|std140}}.
+
{{warning|Implementations sometimes get the {{code|std140}} layout wrong for {{code|vec3}} components. You are advised to manually pad your structures/arrays out and avoid using {{code|vec3}} at all.}}
  
=== Block binding ===
+
'''{{code|std430}}''': This layout works like {{code|std140}}, except with a few optimizations in the base offset and alignment for arrays and structs of scalars and vector elements. Note that this layout can ''only'' be used with shader storage blocks, not uniform blocks.
  
Each uniform/shader storage block has a binding index. This index references one of a number of slots in the OpenGL context that say which buffer objects to use. When a buffer object is bound to the index reference by a block, then that block will get its data from that buffer.
+
==== Layout query ====
 +
{{todo}}
  
If GL 4.2 or ARB_shading_language_420pack is defined, then the block binding indices can be set directly from the shader:
+
The {{code|packed}} and {{code|shared}} layout types allow the implementation to decide what the layout will be. In those cases, [[Program Introspection]] must be used to determine the specifics of the layout. Note that while [[Uniform Introspection|uniform blocks have an older API]] for this, shader storage blocks rely on the [[Program Interface Query]] API. The following explanation of these parameters will refer specifically to the interface query API, but the uniform-specific API has similar information.
 +
 
 +
Each active variable within the block has an offset, relative to the front of the block. This is the number of bytes (more formally, "basic machine units") from the beginning of the buffer to the memory location for this variable.
 +
 
 +
For elements that are aggregated into arrays, there is the notion of a ''stride''.
 +
 
 +
==== Explicit variable layout ====
 +
{{infobox feature
 +
| name = Explicit Variable Layout
 +
| core = 4.4
 +
| core_extension = {{extref|enhanced_layouts}}
 +
}}
 +
{{todo}}
 +
{{clear float}}
 +
 
 +
=== Block buffer binding ===
 +
 
 +
Buffer-backed interface blocks get their data from [[Buffer Object]]s. The association between a buffer object and an interface block works as follows. The GLSL [[Program Object]] does not store buffer objects directly. It works with them indirectly via the [[OpenGL Context]].
 +
 
 +
For each kind of buffer-backed interface block (uniform or shader storage), the OpenGL context has an [[Buffer Object#Binding indexed targets|indexed target for buffer object binding]]. For uniforms, the location is {{enum|GL_UNIFORM_BUFFER}}; for shader storage,  it is {{enum|GL_SHADER_STORAGE_BUFFER}}. Each indexed binding target has a maximum number of indices.
 +
 
 +
For each buffer-backed interface block in a program, the program object stores the index where this interface block gets its data. Two blocks cannot use the same index.
 +
 
 +
The index for a particular block can be specified from the OpenGL API. Alternatively, if {{require|4.2|shading_language_420pack}} is available, it can be set directly in the shader.
 +
 
 +
==== OpenGL binding index setting ====
 +
 
 +
To set the binding index from OpenGL, you must first use [[Program Introspection]] to fetch the interface block's index. This identifier is used to refer to the particular block. Individual blocks within a block array have different block indices; these block indicies need not be contiguous.
 +
 
 +
{{note|Please be aware of the terminology here. "''Block index''" is the number that refers to a specific interface block. "''Binding index''" is an index into an indexed buffer object target.}}
 +
 
 +
The index for a particular block can be found using the [[Program Interface Query]] API. This API is required for shader storage blocks; uniform blocks have an alternate way to access the block index. That is done via this function:
 +
 
 +
{{funcdef|GLuint {{apifunc|glGetUniformBlockIndex}}( GLuint {{param|program​}}, const char *{{param|name​}} );}}
 +
 
 +
If {{param|name}} specifies a block that is inactive, or specifies a block that isn't defined in {{param|program}}, then {{enum|GL_INVALID_INDEX}} is returned. The name is the buffer name, followed by an array subscript if it is an arrayed block.
 +
 
 +
{{note|Block indices for arrays of blocks are not necessarily contiguous. So you have to ask for each one individually.}}
 +
 
 +
Once the index is retrieved, the binding index can be set for that buffer:
 +
 
 +
{{funcdef|void {{apifunc|glUniformBlockBinding}}(GLuint {{param|program​}}, GLuint {{param|uniformBlockIndex​}}, GLuint {{param|uniformBlockBinding​}});
 +
|void {{apifunc|glShaderStorageBlockBinding}}(GLuint {{param|program​}}, GLuint {{param|storageBlockIndex​}}, GLuint {{param|storageBlockBinding​}});}}
 +
 
 +
These two function operate for the two types of buffer-backed blocks. The third parameter is the index that the appropriate buffer object will be bound.
 +
 
 +
==== Shader binding index setting ====
 +
 
 +
The block binding indices can be set directly from the shader:
  
 
<source lang=glsl>
 
<source lang=glsl>
Line 210: Line 288:
 
Uniform blocks cannot use {{code|std430}} layout.
 
Uniform blocks cannot use {{code|std430}} layout.
  
To set the block binding from OpenGL, you must first get the index for that uniform block. To do that, you may use one of these two functions:
+
In a relatively recent change, the rules in {{require|4.3}} for aggregating uniform block definitions in different shaders have changed. If a uniform block (and ''only'' a uniform block. Not other forms of interface blocks) uses an instance name, then all references in the linked program to that uniform block name must also use an instance name. They don't have to use the ''same'' instance name, just some instance name. This way, all code that uses a uniform block in a particular program will scope their variables in the same way (though again, not necessarily with the same instance name).
 
+
GLuint {{apifunc|glGetProgramResourceIndex}}( GLuint {{param|program​}}, GLenum {{param|programInterface​}}, const char *{{param|name}} );
+
GLuint {{apifunc|glGetUniformBlockIndex}}( GLuint {{param|program​}}, const char *{{param|name​}} );
+
 
+
The first function is only available in GL version 4.3 or if ARB_program_interface_query is available. The latter is always available. If you use {{apifunc|glGetProgramResourceIndex}}, the {{param|programInterface​}} parameter should be {{enum|GL_UNIFORM_BLOCK}}.
+
 
+
If {{param|name}} specifies a block that is inactive, or specifies a block that isn't defined in {{param|program}}, then {{enum|GL_INVALID_INDEX}} is returned.
+
 
+
Once the index is retrieved, it can be used to set the buffer binding with this function:
+
 
+
void {{apifunc|glUniformBlockBinding}}( GLuint {{param|program​}}, GLuint {{param|uniformBlockIndex​}}, GLuint {{param|uniformBlockBinding​}} );
+
 
+
This causes the uniform block specified by {{param|uniformBlockIndex​}} in {{param|program​}} to use the uniform buffer binding location {{param|uniformBlockBinding​}}.
+
 
+
There are only {{enum|GL_MAX_UNIFORM_BUFFER_BINDINGS}} binding locations available. So {{param|uniformBlockBinding​}} must be less than this value.
+
  
 
=== Shader storage blocks ===
 
=== Shader storage blocks ===
Line 247: Line 310:
 
There will be (128 - 64) / 4, or 16 elements in {{code|lotsOfFloats}}. Thus, {{code|lotsOfFloats.length()}} will return 16. It will return a different value depending on the size of the bound range.
 
There will be (128 - 64) / 4, or 16 elements in {{code|lotsOfFloats}}. Thus, {{code|lotsOfFloats.length()}} will return 16. It will return a different value depending on the size of the bound range.
  
==== Manual block binding ====
+
{{note|Normally, {{code|length}} in GLSL is a [[Constant Expression]]. However, in this specific case, it is not. It is a [[Dynamically Uniform Expression]].}}
 
+
As with uniform blocks, you must get the index to the shader storage block in order to manually bind it. There is only one function to do this:
+
 
+
GLuint {{apifunc|glGetProgramResourceIndex}}( GLuint {{param|program​}}, GLenum {{param|programInterface​}}, const char *{{param|name}} );
+
 
+
In this case, {{param|programInterface}} should be {{enum|GL_SHADER_STORAGE_BLOCK​}}.  
+
 
+
If {{param|name}} specifies a block that is inactive, or specifies a block that isn't defined in {{param|program}}, then {{enum|GL_INVALID_INDEX}} is returned.
+
 
+
Once the index is retrieved, it can be used to set the buffer binding with this function:
+
 
+
void {{apifunc|glShaderStorageBlockBinding}}( GLuint {{param|program​}}, GLuint {{param|storageBlockIndex​}}, GLuint {{param|storageBlockBinding​}} )
+
 
+
This causes the shader storage block specified by {{param|storageBlockIndex​}} in {{param|program​}} to use the shader storage buffer binding location {{param|storageBlockBinding​}}.
+
 
+
There are only {{enum|GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS}} binding locations available. So {{param|storageBlockBinding​}} must be less than this value.
+
 
+
  
 
[[Category:OpenGL Shading Language]]
 
[[Category:OpenGL Shading Language]]

Latest revision as of 12:35, 6 May 2015

Interface Block (GLSL)
Core in version 4.5
Core since version 3.1
Core ARB extension ARB_uniform_buffer_object, ARB_shader_storage_buffer_object

An Interface Block is a group of GLSL input, output, uniform, or storage buffer variables. These blocks have special syntax and semantics that can be applied to them.

Syntax

Interface blocks have different meanings in their various uses, but they have the same syntax regardless of how they are used. Interface blocks are defined as follows:

storage_qualifier block_name
{
  <define members here>
} instance_name;

This looks like a struct definition, but it is not.

storage_qualifier​ can be one of in​, out​, uniform​, or buffer​ (the latter requires GL 4.3). This defines what kind of interface block is being created.

block_name​ is the true name for the interface block. When the block is referenced in OpenGL code or otherwise talked about in most contexts, this is the name that is used to refer to it. A shader cannot have multiple blocks that have the same block name and the same storage_qualifier​.

instance_name​ is a GLSL name for one or more instances of the block named block_name​. It is optional; if it is present, then all GLSL variables defined within the block must be qualified with the instance name. For example, this defines a uniform block:

uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
} matrices;

To access the projection​ member of this block, you must use matrices.projection​.

If it were defined as follows, without an interface name:

uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
};

You simply use projection​ to refer to it. So the interface name acts as a namespace qualifier. At no time can you use MatrixBlock.projection​ in GLSL (though this is the name that it will appear under when introspecting from OpenGL code).

Because these names are defined globally, they can conflict with other names:

uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
};
 
uniform vec3 modelview;  //Redefining variable. Compile error.

If MatrixBlock​ had been qualified with an instance name, there would be no compiler error.

The instance name can also be used to define an array of blocks:

uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
} matrices[3];

This creates 3 separate interface blocks: matrices[0]​, matrices[1]​, and matrices[2]​. These can have separate binding locations (see below), so they can come from different buffer objects.

For uniform and shader storage blocks, the array index must be a dynamically-uniform integral expression. For other kinds of blocks, they can be any arbitrary integral expression.

Note: The instance name is only used by GLSL. OpenGL always uses the actual block name. Thus, the name of the above block is "MatrixBlock[1]​", and the names of its members are "MatrixBlock.projection​" and so forth.

Linking between shader stages allows multiple shaders to use the same block. Interface blocks match with each other based on the block name and the member field definitions. So the same block in different shader stages can have different instance names.

Valid types

Interface blocks cannot contain opaque types. They also cannot contain nested structure definitions, though block members can contain structure members. Just not definitions of new structs. So define them first, then use them within the interface.

Qualifiers

Members of interface blocks can have type qualifiers associated with them. Some of these may be layout qualifiers. For the most part, the available type qualifiers are the set of the qualifiers that would be allowed for non-block variables of that type.

Qualifiers are applied to a block member as they normally would be for a global definition: listed before the variable name in-order:

in BlockName
{
  flat ivec3 someInts; //Flat interpolation.
  vec4 value;          //Default interpolation is smooth.
};

In some cases, members of an interface block cannot use the qualifiers allowable to global definitions. And in some cases, interface block members have additional qualifiers, typically layout qualifiers to control aspects of the variable's layout within the block.

Interface matching

Sometimes, it is important for two or more interface block definitions to match with one another. Two blocks are said to match if they define the exact same variables (type/array count and name), in the exact same order, and with the exact same variable qualifiers.

For buffer-backed storage blocks, two blocks that match can, with the proper qualifiers, refer to identical memory layouts. Furthermore, if two blocks in the same program (but different shader stages) match and have the same block name, then the program will only advertise one such block to the user. This is a lot like how Uniforms with the same names and types are merged into one between stages.

For input or output qualifiers, matching block definitions are important for interface matching between shaders in the same pipeline. Interface matching with interface blocks also requires that the output from the producing stage and the input from the consuming stage use the same block name. Also, the array count of the block, when declared as an array of blocks, must be correct. More details are found in the above linked page.

Input and output

Input and output blocks are designed to complement each other. Their primary utility is with geometry or tessellation shaders, as these shaders often work with aggregates of input/output values. Blocks make it easy to organize this data.

Interface blocks for inputs/outputs can only be used to aggregate data interfaces between shader stages. Vertex shaders cannot declare an input interface block, and fragment shaders cannot declare an output interface block.

If a shader stage uses an input block and it is linked directly to its previous shader stage, then that stage must provide a matching output block, as defined above. For example, a vertex shader can pass data to a geometry shader using these block definitions:

//Vertex Shader
out VertexData
{
  vec3 color;
  vec2 texCoord;
} outData;
 
//Geometry Shader
in VertexData
{
  vec3 color;
  vec2 texCoord;
} inData[];

Notice that the geometry shader block is defined as an array in the geometry shader. It also uses a different instance name. These work perfectly fine; the GS will receive a number of vertices based on the primitive type it is designed to take.

Note: Input/output interface blocks don't use location​ names for interface matching. So in separable programs, interfaces must match directly by name.

The only types that are valid in input/output blocks are those which are valid as input/output variables.

Buffer backed

Uniform blocks and shader storage blocks work in very similar ways, so this section will explain the features they have in common. Collectively, these are called "buffer-backed blocks" because the storage for their contents come from a Buffer Object.

Note that shader storage blocks require OpenGL 4.3 or ARB_shader_storage_buffer_objects.

Matrix storage order

Because the storage for these blocks comes from Buffer Objects, matrix ordering becomes important. Matrices can be stored in column or row-major ordering. Layout qualifiers are used to decide which is used on a per-variable basis.

Note that this does not change how GLSL works with them. GLSL matrices are always column-major. This specification only changes how GLSL fetches the data from the buffer.

Defaults can be set with this syntax:

layout(row_major) uniform;

From this point on, all matrices in uniform blocks are considered row-major. Shader storage blocks are not affected; they would need their own definition (layout(row_major) buffer;​).

A particular block can have a default set as well:

layout(row_major) uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
} matrices[3];

All of the matrices are stored in row-major ordering.

Individual variables can be adjusted as well:

layout(row_major) uniform MatrixBlock
{
  mat4 projection;
  layout(column_major) mat4 modelview;
} matrices[3];

MatrixBlock.projection​ is row-major, but MatrixBlock.modelview​ is column-major. The default is column-major.

Memory layout

The specific size of basic types used by members of buffer-backed blocks is defined by OpenGL. However, implementations are allowed some latitude when assigning padding between members, as well as reasonable freedom to optimize away unused members. How much freedom implementations are allowed for specific blocks can be changed.

There are four memory layout qualifiers: shared​, packed​, std140​, and std430​. Defaults can be set the same as for matrix ordering (eg: layout(packed) buffer;​ sets all shader storage buffer blocks to use packed​). The default is shared​.

packed​: This layout type means that the implementation determines everything about how the fields are laid out in the block. The OpenGL API can be used to query the layout for the members of a particular block. Each member of a block will have a particular byte offset, which you can use to determine how to upload its data. Also, members of a block can be optimized out if they are found by the implementation to not affect the result of the shader. Therefore, the active components of a block may not be all of the components it was defined with.

shared​: This layout type works like packed​, with two exceptions. First, it guarantees that all of the variables defined in the block are considered active; this means nothing is optimized out. Second, it guarantees that the members of the block will have the same layout as a block definition in another program, so long as:

  • The blocks in different programs use the exact same block definition (ignoring differences in variable names and the block name itself). Be careful about specifying the default matrix storage order, as this can implicitly change the definition of blocks following them.
  • All members of the block use explicit sizes for arrays.

Because of these guarantees, buffer-backed blocks declared shared​ can be used with any program that defines a block with the same elements in the same order. This even works across different types of buffer-backed blocks. You can use a buffer as a uniform buffer at one point, then use it as a shader storage buffer in another. OpenGL guarantees that all of the offsets and alignments will match between two shared​ blocks that define the same members in the same order. In short, it allows the user to share buffers between multiple programs.

std140​: This layout alleviates the need to query the offsets for definitions. The rules of std140​ layout explicitly state the layout arrangement of any interface block declared with this layout. This also means that such an interface block can be shared across programs, much like shared​. The only downside to this layout type is that the rules for packing elements into arrays/structs can introduce a lot of unnecessary padding.

The rules for std140​ layout are covered quite well in the OpenGL specification (OpenGL 4.5, Section 7.6.2.2, page 137). Among the most important is the fact that arrays of types are not necessarily tightly packed. An array of float​s in such a block will not be the equivalent to an array of floats​ in C/C++. The array stride (the bytes between array elements) is always rounded up to the size of a vec4​ (ie: 16-bytes). So arrays will only match their C/C++ definitions if the type is a multiple of 16 bytes

Warning: Implementations sometimes get the std140​ layout wrong for vec3​ components. You are advised to manually pad your structures/arrays out and avoid using vec3​ at all.

std430​: This layout works like std140​, except with a few optimizations in the base offset and alignment for arrays and structs of scalars and vector elements. Note that this layout can only be used with shader storage blocks, not uniform blocks.

Layout query

The packed​ and shared​ layout types allow the implementation to decide what the layout will be. In those cases, Program Introspection must be used to determine the specifics of the layout. Note that while uniform blocks have an older API for this, shader storage blocks rely on the Program Interface Query API. The following explanation of these parameters will refer specifically to the interface query API, but the uniform-specific API has similar information.

Each active variable within the block has an offset, relative to the front of the block. This is the number of bytes (more formally, "basic machine units") from the beginning of the buffer to the memory location for this variable.

For elements that are aggregated into arrays, there is the notion of a stride.

Explicit variable layout

Explicit Variable Layout
Core in version 4.5
Core since version 4.4
Core ARB extension ARB_enhanced_layouts


Block buffer binding

Buffer-backed interface blocks get their data from Buffer Objects. The association between a buffer object and an interface block works as follows. The GLSL Program Object does not store buffer objects directly. It works with them indirectly via the OpenGL Context.

For each kind of buffer-backed interface block (uniform or shader storage), the OpenGL context has an indexed target for buffer object binding. For uniforms, the location is GL_UNIFORM_BUFFER; for shader storage, it is GL_SHADER_STORAGE_BUFFER. Each indexed binding target has a maximum number of indices.

For each buffer-backed interface block in a program, the program object stores the index where this interface block gets its data. Two blocks cannot use the same index.

The index for a particular block can be specified from the OpenGL API. Alternatively, if OpenGL 4.2 or ARB_shading_language_420pack is available, it can be set directly in the shader.

OpenGL binding index setting

To set the binding index from OpenGL, you must first use Program Introspection to fetch the interface block's index. This identifier is used to refer to the particular block. Individual blocks within a block array have different block indices; these block indicies need not be contiguous.

Note: Please be aware of the terminology here. "Block index" is the number that refers to a specific interface block. "Binding index" is an index into an indexed buffer object target.

The index for a particular block can be found using the Program Interface Query API. This API is required for shader storage blocks; uniform blocks have an alternate way to access the block index. That is done via this function:

GLuint glGetUniformBlockIndex( GLuint program​​, const char *name​​ );

If name​ specifies a block that is inactive, or specifies a block that isn't defined in program​, then GL_INVALID_INDEX is returned. The name is the buffer name, followed by an array subscript if it is an arrayed block.

Note: Block indices for arrays of blocks are not necessarily contiguous. So you have to ask for each one individually.

Once the index is retrieved, the binding index can be set for that buffer:

void glUniformBlockBinding(GLuint program​​, GLuint uniformBlockIndex​​, GLuint uniformBlockBinding​​);

void glShaderStorageBlockBinding(GLuint program​​, GLuint storageBlockIndex​​, GLuint storageBlockBinding​​);

These two function operate for the two types of buffer-backed blocks. The third parameter is the index that the appropriate buffer object will be bound.

Shader binding index setting

The block binding indices can be set directly from the shader:

layout(binding = 3) uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
};

Uniform and shader storage blocks have a different set of indices. Uniform block binding indices refer to blocks bound to indices in the GL_UNIFORM_BUFFER indexed target with glBindBufferRange. Shader storage block binding indices refer to blocks bound to indices in the GL_SHADER_STORAGE_BUFFER target.

For arrays of blocks, the binding syntax sequentially allocates indices. So this definition:

layout(binding = 2) uniform MatrixBlock
{
  mat4 projection;
  mat4 modelview;
} matrices[4];

There will be 4 separate blocks, which use the binding indices 2, 3, 4, and 5.

Block bindings can also be set manually from OpenGL. The function to do this depends on whether it is a uniform block or a shader storage block. See their individual sections.

Once a binding is assigned, the storage can be bound to the OpenGL context with glBindBufferRange (or glBindBufferBase for the whole buffer). Each type of buffer-backed block has its own target. Uniform blocks use GL_UNIFORM_BUFFER, and shader storage blocks use GL_SHADER_STORAGE_BUFFER.

Uniform blocks

Uniform blocks cannot use std430​ layout.

In a relatively recent change, the rules in OpenGL 4.3 for aggregating uniform block definitions in different shaders have changed. If a uniform block (and only a uniform block. Not other forms of interface blocks) uses an instance name, then all references in the linked program to that uniform block name must also use an instance name. They don't have to use the same instance name, just some instance name. This way, all code that uses a uniform block in a particular program will scope their variables in the same way (though again, not necessarily with the same instance name).

Shader storage blocks

If the last member variable of a shader storage block is declared with an indeterminate array length (using []​), then the size of this array is determined at the time the shader is executed. The size is basically the rest of the buffer object range that was attached to that binding point.

For example, consider the following:

layout(std430, binding = 2) buffer MyBuffer
{
  mat4 matrix;
  float lotsOfFloats[];
};

The number of float​ variables in lotsOfFloats​ depends on the size of the buffer range that is attached to the binding point. matrix​ will be 64 bytes in size, so the number of elements in lotsOfFloats​ will be (size of the bound range minus 64) / 4. Thus, if we bind with this:

glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, buffer, 0, 128);

There will be (128 - 64) / 4, or 16 elements in lotsOfFloats​. Thus, lotsOfFloats.length()​ will return 16. It will return a different value depending on the size of the bound range.

Note: Normally, length​ in GLSL is a Constant Expression. However, in this specific case, it is not. It is a Dynamically Uniform Expression.