What Preprocessor Directives Does GLSL Support?

The OpenGL Shading Language (GLSL) features a built-in preprocessor closely modeled on the standard C/C++ preprocessor, used to control compilation conditionally, define macros, enforce language versions, and manage hardware extensions. While it supports familiar operations like conditional branching and macro expansion, it includes GLSL-specific directives like #version and #extension, while omitting standard features like #include from the core specification.

Macro Definition and Undefinition

GLSL allows basic macro replacement and parameterized macros across shader code.

  • #define: Defines a macro identifier or a function-like macro. Macro definitions do not evaluate types and perform direct token substitution.
  • #undef: Undefines an existing macro identifier, removing its definition from subsequent shader lines.

GLSL also provides standard predefined macros, such as __LINE__, __FILE__, __VERSION__, and GL_core_profile (or GL_es_profile), which allow shaders to adapt dynamically to their execution context.

Conditional Compilation

Conditional directives enable shaders to include or exclude blocks of code based on macro definitions or integer constant expressions.

  • #if: Evaluates an integer constant expression. Non-zero evaluates to true.
  • #ifdef: Checks if a specific macro has been defined.
  • #ifndef: Checks if a specific macro has not been defined.
  • #elif: Short for "else if," evaluated if preceding #if or #elif conditions fail.
  • #else: Executes if all preceding condition branches evaluate to false.
  • #endif: Terminates a conditional compilation block.
  • defined: A preprocessor operator used inside #if or #elif expressions (e.g., defined(FEATURE_FLAG)) to evaluate whether a macro name is defined.

Floating-point operations and typecasts are not permitted within GLSL preprocessor conditional expressions; only integer constant expressions and operators (such as arithmetic, bitwise, and logical operators) are valid.

Version Declaration: #version

The #version directive is mandatory for modern GLSL and informs the driver which specification version to use when parsing the shader.

  • Syntax: #version number [profile]
  • Placement: Must be the first non-comment, non-whitespace statement in the shader source string.
  • Profiles: On desktop OpenGL 3.3 and higher, an optional profile argument can be specified (core, compatibility, or es).

For example, #version 330 core specifies version 3.30 using the core profile, whereas #version 300 es designates OpenGL ES 3.0.

Extension Handling: #extension

The #extension directive controls compiler behavior regarding optional or vendor-specific GLSL capabilities.

  • Syntax: #extension extension_name : behavior
  • Target: extension_name can be a specific extension (e.g., GL_ARB_gpu_shader5) or all to apply a rule across all available extensions.
  • Behaviors:
  • require: Fails compilation with an error if the extension is not supported.
  • enable: Enables the extension if supported; issues a warning if unsupported.
  • warn: Enables the extension if supported, but emits a compiler warning whenever the extension is used.
  • disable: Disables use of the extension and emits an error if any of its features are referenced.

Diagnostic and Line Control Directives

GLSL includes directives to adjust compiler error tracking and report custom compilation errors.

  • #error: Halts compilation immediately and prints the supplied token message to the shader compilation info log.
  • #line: Resets the internal line number and optionally the source string index tracked by the compiler (#line line_number [source_string_number]). This is useful when host applications concatenate multiple source strings or generate code dynamically.

Compiler Pragma Controls: #pragma

The #pragma directive passes implementation-specific instructions to the shader compiler. Standard GLSL specifications define several reserved pragmas:

  • #pragma STDGL: Reserved for standard GLSL pragma configurations.
  • #pragma optimize(on) / #pragma optimize(off): Directs the compiler to enable or disable optimization passes (primarily used for debugging).
  • #pragma debug(on) / #pragma debug(off): Requests debug instrumentation from the driver to assist external shader debuggers.
  • #pragma invariant(all): Forces all output variables in the shader stage to be treated with invariant qualification, guaranteeing identical calculations across different shaders.

Omitted Directives

The core GLSL specification does not include a standard #include directive. Shader file composition, module management, and header inclusions must either be resolved on the CPU before passing the shader string to glShaderSource, or handled using specialized extensions like GL_ARB_shading_language_include or GL_GOOGLE_include_directive where driver and runtime support exist.