Glslang Preprocessor Macro Injection Using -D
This article provides a technical overview of how
glslang processes and injects preprocessor macros via the
-D command-line argument. It covers the command syntax, the
compiler's internal preamble handling, the interaction with GLSL
#version constraints, and best practices for configuring
shader build pipelines.
The Purpose of the -D Flag
The glslangValidator and glslang
command-line tools support the -D option to define
preprocessor macros prior to compiling a shader. This mirrors standard
C/C++ compiler behavior (such as gcc or
clang), allowing developers to compile multiple
permutations of a single shader—such as enabling debug modes, setting
feature flags, or toggling platform-specific optimizations—without
altering the underlying source files.
Syntax and Argument Parsing
The -D switch can be used in two primary forms:
Boolean/Existence Definitions:
glslangValidator -DENABLE_FOG shader.frag -V -o shader.spvWhen no value is assigned,
glslangdefines the identifier with a default value of1. This is equivalent to inserting#define ENABLE_FOG 1.Key-Value Definitions:
glslangValidator -DMAX_LIGHTS=8 -DQUALITY_LEVEL=2 shader.frag -V -o shader.spvThis assigns the specified literal or token to the identifier. Equivalent to
#define MAX_LIGHTS 8.
Multiple -D arguments can be chained in a single
command. The definitions are evaluated in the order they are passed on
the command line.
Internal Handling and the #version Directive
In GLSL, the #version directive must strictly appear
before any other non-whitespace, non-comment tokens in the source file.
Naively prepending #define statements to the source file
would cause standard-compliant GLSL parsers to emit an error.
glslang avoids this issue through internal preamble
management:
- Symbol Table Pre-population: Rather than performing
raw text concatenation at the beginning of the file,
glslangstores command-line macros in its internal preprocessor symbol table. - Preamble Injection: In workflows using the C++ API
(
glslang::TShader), definitions can be supplied viaTShader::setPreamble(). The compiler engine guarantees that injected preamble tokens are processed in the proper preprocessor context without violating the requirement that#versionremains the first effective directive in the input source.
Handling HLSL vs. GLSL
When using glslang to compile HLSL via the
-D argument, standard C-preprocessor rules apply directly
without the strict #version positioning constraints of
GLSL. In this mode, injected definitions immediately populate the global
macro definition table before parsing the entry point.
Common Caveats
Spaces Around Equals Signs: Shell parsers require arguments to remain intact. Commands like
-D MAX_LIGHTS = 8will fail. The syntax must not contain unescaped spaces:-DMAX_LIGHTS=8.String Literals: To define a macro that expands to a string literal, quotes must be escaped so the shell does not strip them:
glslangValidator -DTEXTURE_NAME=\"diffuse.png\" shader.frag -VShader-Level
#undef: A macro injected through-Dcan still be undefined inside the shader code using#undef. Compiler injection establishes the initial state, but does not make the identifier immutable during preprocessing.