Selecting a Shading Language
The various extensions and core revisions of the OpenGL API have led to the availability of a number of different potential shading languages that you may use. This page will deal with what they are, their pros and cons, and hardware.
- 1 Main options
- 2 OpenGL shading language
- 3 SPIR-V
- 4 C for graphics
- 5 ARB assembly
- 6 NVIDIA assembly
- 7 Special considerations
While there are other possibilities, these are the three primary competitors in the OpenGL space:
- OpenGL Shading Language (GLSL)
- C for Graphics (Cg)
- ARB assembly: assembly-like languages that are accessed through a set of ARB extensions.
- NVIDIA assembly: NVIDIA-specific extensions to the ARB assembly language.
OpenGL shading language
The OpenGL Shading Language (GLSL) is a "high-level" language. This means high-level compared to assembly, not high-level the way a language like C# is compared to C. GLSL is structurally very similar to C.
This is the current standard, as far as OpenGL is concerned. It is one of only two shading languages (SPIR-V being the other) that is a part of the OpenGL specification.
Because of this, it is kept up-to-date with current OpenGL features. Each new version of the base standard usually means a new version number of GLSL as well.
The complexity of having a full C-style language in a graphics driver causes quite a few driver bugs to show themselves, particularly in ATI compilers.
NVIDIA's GLSL compiler is really a slight modification of the Cg compiler. Because of that, some of the differences between GLSL and Cg will occasionally show their heads. Cg is more permissive syntactically than GLSL, so a GLSL program that compiles on an NVIDIA driver may not compile on an ATI driver. It can also give unusual error messages, with references to a "profile" (a concept that exists in Cg but not GLSL).
SPIR-V is an intermediate shading language developed by the Khronos Group (the organization also responsible for OpenGL and other standards). Though initially developed for Vulkan and OpenCL, the ability to give OpenGL a SPIR-V shader was also added to OpenGL in 4.6.
SPIR-V is an intermediate language, so it lacks some of the higher-level constructs that often challenge GLSL compilers. As an intermediate language, many shading languages can compile directly to it. The glslang project provides an off-line compiler tool that can convert GLSL into SPIR-V compatible with OpenGL.
SPIR-V also has a developing ecosystem around supporting it. Glslang itself even provides HLSL support (though it's not clear if HLSL-to-SPIR-V for OpenGL is effective). There are tools to perform introspection over SPIR-V shader files, to manipulate and combine them, and so forth.
Though SPIR-V as a language is compatible with several backends, it is not possible to write a single shader (without #defines) which can be consumed by both Vulkan and OpenGL. Their resource binding models are too dissimilar, so you will generally need to keep at least the resource definitions in different files.
SPIR-V also requires a lot more explicit specification than GLSL, which often means that if you use GLSL as your initial source that gets compiled to SPIR-V, you will be required to provide similar explicit specifications as well. For example, you need to use location qualifiers on all inter-stage inputs and outputs.
C for graphics
Cg is NVIDIA's shading language. It was originally intended for OpenGL, but the ARB rejected it in favor of GLSL. It is at this point defunct (NVIDIA no longer supports it), and you should probably ignore it.
The syntax is almost identical to Direct3D's HLSL. As such, Cg shaders can be ported without changes to HLSL, if one is doing cross-API development.
The Cg compiler is a library that is external to the driver. As such, the user can pre-compile shaders into their target form.
The Cg compiler is really a cross-compiler. The source code is compiled into an output based on a profile. To use Cg in OpenGL, your output profile must be something that OpenGL can accept. Therefore, Cg's disadvantages depend entirely on what profile you are using.
The GLSL profile causes the use of Cg to gain the disadvantages of GLSL, but also the advantages of it. The same goes for the ARB assembly and NVIDIA assembly profiles.
Without precompiling, you also gain a increased compile time. First the Cg compiler must generate the output for the profile, then the GLSL or ARB assembly compiler must operate to create the actual shader.
Additionally, you lose some of the flexibility of using GLSL directly. For example, you can use extensions to GLSL, but only if you're using it directly. You can't use extensions through Cg.
Another problem, particularly when using GLSL profiles, is that the code generated may not be the best for all platforms. NVIDIA writes the profiles, after all. As such, they can optimize for things their hardware does well, but would be less optimal for other hardware. What you get out of the GLSL profile is not necessarily what you would have written yourself, though it may be close to it.
The ARB_vertex_program and ARB_fragment_program extensions expose a assembly-like shading language. It is not pure assembly, and implementations of it do not have to resemble the language. But it is something close.
Faster compile times.
Intel supports these.
ARB assembly predates GLSL version 1.0. As such, ARB assembly is very old. It was designed to work with hardware around the time of the Radeon 9xxx and the GeForce FX 5xxx series. This means, in Direct3D terms, that ARB assembly only provides shader model 2.0-level functionality. It has no support for many common features.
Being an assembly-like language, it is more difficult to program in as your programs become more complex.
NVIDIA assembly refers to a number of NVIDIA-specific extensions to the ARB assembly language. They make the language congruent with modern GL 3.x features (geometry shaders, etc).
Compilation speed similar to that of ARB assembly.
Intel is the sales leader in dedicated graphics hardware for PCs. This hardware is not standalone graphics boards, but low-end graphics chips integrated in Intel's motherboards.
Put simply, Intel's driver support is terrible (note: this is true for both Direct3D and OpenGL, though GL gets it worse). They refuse to implement GLSL on any of their D3D9 chips. They have implemented GLSL on their D3D10 parts, but even then, they only implement OpenGL version 2.0 (D3D10 is the functional equivalent of version 3.2 of OpenGL).
If you are interested in your shader-based programs working on older Intel hardware, the ARB assembly is your best bet, as Intel's OpenGL drivers do support that.