From OpenGL Wiki
Jump to: navigation, search

SPIR-V's compilation model looks similar to that of GLSL, but it does have some unique characteristics.

As with GLSL, SPIR-V makes use of shader and program objects. Because SPIR-V is a binary format, SPIR-V shaders are loaded into shader objects via the use of the shader binary API:

void glShaderBinary(GLsizei count​, const GLuint *shaders​, GLenum binaryFormat​, const void *binary​, GLsizei length​);

shaders​ is an array of count​ length of previously created shader objects that the SPIR-V data will be loaded into. So this function can load the same SPIR-V source code into multiple shader objects.

SPIR-V has a specific binaryFormat​: the enumerator GL_SHADER_BINARY_FORMAT_SPIR_V. The binary​ is the loaded SPIR-V itself, with a byte length of length​. This must include the entire SPIR-V, as defined by the specification, including header information.

The use of this function will replace the shaders specified by previous calls to glShaderSource or glShaderBinary. Loading a non-SPIR-V binary or loading GLSL source strings into the program will make it no longer contain SPIR-V code.

While a shader object has a SPIR-V binary loaded into it, the object becomes slightly different. glGetShaderiv(shader, GL_SPIR_V_BINARY) will return GL_TRUE.

Entry points and specialization

SPIR-V is similar to GLSL, but it has some differences. Two differences are particularly relevant.

  1. A single SPIR-V file can have function entry-points for multiple shader stages, even of different types.
  2. SPIR-V has the concept of "specialization constants": parameters which the user can provide before the SPIR-V is compiled into its final form.

Before a SPIR-V shader object can be used, you must specify which entry-point to use and provide values for any specialization constants used by that entry-point. This is done through a single function:

void glSpecializeShader(GLuint shader​, const GLchar *pEntryPoint​, GLuint numSpecializationConstants​, const GLuint *pConstantIndex​, const GLuint *pConstantValue​);

pEntryPoint​ is the string name of the entry-point that this SPIR-V shader object will use. pConstantIndex​ and pConstantValue​ are arrays containing the index of each specialization constant and the corresponding values that will be used. These arrays are numSpecializationConstants​ in length. Specialization constants not referenced by pConstantIndex​.

Specializing a SPIR-V shader is analogous to compiling a GLSL shader. So if this function completes successfully, the shader object's compile status is GL_TRUE. If specialization fails, then the shader infolog has information explaining why and an OpenGL Error is generated.

pEntryPoint​ must name a valid entry point. Also, the entry point's "execution model" (SPIR-V speak for "Shader Stage") must match the stage the shader object was created with. Specialization can also fail if pConstantIndex​ references a specialization constant index that the SPIR-V binary does not use. If specialization fails, the shader's info log is updated appropriately.

Once specialized, SPIR-V shaders cannot be re-specialized. However, you can reload the SPIR-V binary data into them, which will allow them to be specialized again.

Linking SPIR-V

SPIR-V shader objects that have been specialized can be used to link programs (separable or otherwise). If you link multiple shader objects in the same program, then either all of them must be SPIR-V shaders or none of them may be SPIR-V shaders. You cannot link SPIR-V to non-SPIR-V shaders in a program.

Also, note that SPIR-V shaders must have an entry-point. So SPIR-V modules for the same stage cannot be linked together. Each SPIR-V shader object must provide all of the code for its module.

You can use separable programs built from SPIR-V shaders in the same pipeline object as non-SPIR-V shaders.