`GLSLC`(1)

GLSLC(1) β€” aka the glslc manual, but make it make sense πŸ’…

okay bestie, sit down, hydrate, and let me tell you about glslc.


✨ NAME ✨

glslc β€” a command-line GLSL/HLSL to SPIR-V compiler with Clang-compatible arguments.

yes, it compiles your shaders. yes, it takes Clang-style args. yes, it's that girl.


🌸 SYNOPSIS 🌸

glslc [--help]
glslc [-h]

glslc [--show-limits]

glslc [-c|-S|-E]
      [-x ...] [-std=standard]
      [ ... options for resource bindings ... ]
      [-fhlsl-16bit-types]
      [-fhlsl-offsets]
      [-fhlsl-functionality1]
      [-fentry-point=<name>]
      [-fauto-map-locations]
      [-finvert-y]
      [-flimit=...]
      [-flimit-file <resource-limits-file>]
      [-fshader-stage=...]
      [--target-env=...]
      [--target-spv=...]
      [-g]
      [-O0|-Os]
      [-Idirectory...]
      [-Dmacroname[=value]...]
      [-w] [-Werror]
      [-o outfile]
      shader...

πŸ¦‹ DESCRIPTION πŸ¦‹

πŸ’– Input file languages πŸ’–

glslc accepts both GLSL/HLSL source and SPIR-V assembly files as inputs. versatile queen 🫢

✨ Shader stage specification ✨

bestie, glslc gives you three ways to tell it what shader stage your GLSL source file is:

  • -fshader-stage=<stage>
  • #pragma shader_stage(<stage>)
  • the file extension

and the priority order is strict, no cap:

-fshader-stage= overrides #pragma shader_stage(), which overrides the file extension. periodt.

So you can name a file with the right extension, listed in the table below. Or, -fshader-stage= and #pragma shader_stage() let you specify shader stages from the command line and from inside the source file. The possible stages for those are also in the table below. Details on -fshader-stage= live in its own section (go look, it's right there πŸ‘€).

Table 1. Shader Stage Selection β€” ok so the table didn't survive the vibe check, so here it is as a list instead 🌈:

  • shader stage vertex β†’ shader file extension .vert β†’ <stage> value vertex
  • shader stage fragment β†’ shader file extension .frag β†’ <stage> value fragment
  • shader stage tesselation control β†’ shader file extension .tesc β†’ <stage> value tesscontrol
  • shader stage tesselation evaluation β†’ shader file extension .tese β†’ <stage> value tesseval
  • shader stage geometry β†’ shader file extension .geom β†’ <stage> value geometry
  • shader stage compute β†’ shader file extension .comp β†’ <stage> value compute

#pragma shader_stage() relies on the #pragma preprocessor directive β€” which means the token inside shader_stage() is not subject to preprocessor macro expansion. it must be exactly one of the stages in the table above. no improvising. πŸ’…

#pragma shader_stage() behaves as follows:

  • The first #pragma shader_stage() directive in a translation unit must precede any non-preprocessor tokens.
  • If there is more than one #pragma shader_stage() directive in the same translation unit, all the stages specified must be the same. Otherwise, glslc will issue an error. 😭

πŸŽ€ SPIR-V assembly files πŸŽ€

SPIR-V assembly input files should follow the syntax defined in the SPIRV-Tools project:

and they should have the .spvasm extension.

Command line options treat SPIR-V assembly files differently β€” some may ignore them, e.g. -E, -S, and some may even treat them not as SPIR-V assembly files, e.g. -fshader-stage=. messy, i know. πŸ™ˆ

🌸 Output file naming 🌸

If a name is specified via -o, the output file will be given that name. Otherwise:

  • If a compilation stage selection option is given (-S or -c), there will be one output file generated per input shader file. The generated output file will end with a file extension that matches the compilation stage, which is .spvasm for -S and .spv for -c. The name will depend on the original file's name and extension.
    • If the input file has a shader stage selection extension, the output file will be named as by appending the file extension for the compilation stage to the input file's name. E.g., glslc -c foo.vert will generate foo.vert.spv, and glslc -S bar.frag will generate bar.frag.spvasm.
    • Otherwise, the output file will be named as by replacing the input file's file extension, if any, with the file extension for the compilation stage. E.g., glslc -c foo will generate foo.spv, and glslc -S bar.glsl will generate bar.spvasm.
  • If no compilation stage is selected, the output file will be named a.spv.

πŸ‘‘ COMMAND LINE OPTIONS πŸ‘‘

🌈 Overall Options 🌈

  • --help, -h β€” option --help or -h tells the glslc compiler to display all available options and exit.
  • --show-limits β€” --show-limits shows default resource limits for shader compilation. The syntax is the same as accepted by -flimit= and for the contents of the file specified by -flimit-file.
  • -o β€” -o lets you specify the output file's name. It cannot be used when there are multiple files generated. A filename of - represents standard output.

πŸ’œ Language and Mode Selection Options πŸ’œ

  • -finvert-y β€” inverts position.Y output in a vertex shader.
  • -flimit= β€” -flimit=<resource-limits> lets you specify resource limits. The argument should be a sequence of limit name, integer value pairs. Tokens should be separated by whitespace. If the same limit is specified several times, only the last setting takes effect.

Use --show-limits to show the default values, and example syntax.

This option affects all compiled shaders.

  • -flimit-file β€” -flimit-file <resource-limits-file> lets you specify resource limits in a file. The syntax of the file contents is the same as the argument to -flimit= and the output of --show-limits. This option accepts Glslang resource configuration files, e.g. as emitted by glslangValidator -c.

This option affects all compiled shaders.

  • -fshader-stage= β€” -fshader-stage=<stage> lets you specify the shader stage for one or more inputs from the command line.

Possible values for <stage> are listed in the Shader Stage Selection table.

-fshader-stage= behaves as follows:

  • -fshader-stage= sets the shader stage for subsequent input files. It does not affect the stages of any preceding inputs on the command line.
  • When supplying more than one -fshader-stage= argument, the most recent argument preceding an input file applies.
  • A shader file not ending with known shader file extensions must have a -fshader-stage= argument ahead of it to specify its stage.
  • If there is a -fshader-stage= before a file in which there is a #pragma shader_stage() directive, the directive is ignored and the -fshader-stage= argument is used instead.
  • If there is a -fshader-stage= before a file with a known shader file extension, the file extension is ignored and the -fshader-stage= argument is used instead.
Caution ⚠️ -fshader-stage= overrides file extension; that means it should not be used together with SPIR-V assembly files because glslc will treat the given SPIR-V assembly files as GLSL source code of the given shader stage. If you need to supply both SPIR-V assembly files and -fshader-stage= on the same command line, please put SPIR-V assembly files ahead of the first -fshader-stage=, since -fshader-stage= only affects the treatment of subsequent files.
  • -std= β€” -std=<value> lets you specify a shader version and profile on the command line. <value> can be any valid concatenation of a GLSL version number and profile, e.g., 310es, 450core, etc. The profile can be omitted as allowed by GLSL, e.g., 450.

-std= behaves as follows:

  • -std= affects the version of all GLSL inputs passed to glslc.
  • -std= is ignored for HLSL inputs.
  • -std= overwrites #version directives in all input shaders, including those preceding the argument.
  • If a -std= argument specifies a different version from a #version directive in an input file, glslc will issue a warning.
  • If multiple -std= arguments are specified on the command line, only the last one takes effect.
Caution ⚠️ -std= does not affect the #version directive in the preprocessed output. That is, when -std= specifies a version different from the shader source code, the #version directive in preprocessed output will still be the one in the source code. But -std= does affect the behavior of #line directives in the preprocessed output. Behavior of #line directives will follow the version specified by -std=.
  • --target-env= β€” --target-env=<value> lets you specify a target environment on the command line. This affects the generation of warnings and errors. The <value> can be one of the following:
    • vulkan: create SPIR-V under Vulkan 1.0 semantics.
    • vulkan1.0: create SPIR-V under Vulkan 1.0 semantics.
    • vulkan1.1: create SPIR-V under Vulkan 1.1 semantics.
    • vulkan1.2: create SPIR-V under Vulkan 1.2 semantics.
    • vulkan1.3: create SPIR-V under Vulkan 1.3 semantics.
    • vulkan1.4: create SPIR-V under Vulkan 1.4 semantics.
    • opengl: create SPIR-V under OpenGL 4.5 semantics.
    • opengl4.5: create SPIR-V under OpenGL 4.5 semantics.

okay so default-behaviour tea β˜•: generated code uses SPIR-V 1.0, except for code compiled for Vulkan 1.1, which uses SPIR-V 1.3, and code compiled for Vulkan 1.2, which uses SPIR-V 1.5. It's giving glow-up-per-version honestly.

If this option is not specified, a default of vulkan1.0 is used. Periodt.

Note: Support for the OpenGL compatibility profile, opengl_compat, has been removed. πŸ’€ She's gone bestie, we don't talk about her anymore.

  • --target-spv=, --target-spv=<value> β€” lets you specify the SPIR-V version to be used by the generated module ✨. The default is to use the highest version of SPIR-V required to be supported by the target environment. The defaults for specific Vulkan target environments are as follows: SPIR-V 1.0 for Vulkan 1.0, SPIR-V 1.3 for Vulkan 1.1, and SPIR-V 1.5 for Vulkan 1.2.

The <value> can be one of the following:

  • spv1.0
  • spv1.1
  • spv1.2
  • spv1.3
  • spv1.4
  • spv1.5
  • spv1.6
  • -x β€” lets you specify the language of the input shader files. Valid languages are glsl and hlsl. If the file extension is hlsl then the default language is HLSL. Otherwise the default is glsl.
Note: HLSL compilation will use HLSL packing (offset) rules for variables that are vertex shader outputs, and inputs and outputs of both geometry and pixel shaders. πŸ‘€

Compilation Stage Selection Options 🎬

  • -c β€” tells the glslc compiler to run the preprocessing and compiling stage. Each input shader file results in a SPIR-V binary file; these SPIR-V binary files are named by the rules in the Output File Naming section. πŸ“
  • -E β€” tells the glslc compiler to run only the preprocessing stage. It overrides -c and -S. Preprocessed output is written to standard output, while preprocessing errors are written to standard error. If multiple input shader files are given, their preprocessed output are all written to standard output, in the order specified on the command line.

glslc will do nothing for SPIR-V assembly files with this option. Nothing. Zero. Zip. πŸ™ˆ

  • -S β€” tells the glslc compiler to run the preprocessing, compiling, and then disassembling stage. It overrides -c. Each input shader file results in a SPIR-V assembly file; these SPIR-V assembly files are named by the rules in the Output File Naming section. 🌸

glslc will do nothing for SPIR-V assembly files with this option either.

No Compilation Stage Selection πŸ¦‹

If none of the above options is given, the glslc compiler will run preprocessing, compiling, and linking stages.

Warning Linking of multiple input shader files are not supported yet. 😭 Not yet, bestie. Patience.

Preprocessor Options 🧚

  • -D β€” -Dmacroname[=[value]] lets you define a preprocessor macro before input shader files are preprocessed. If value is omitted, the macro is defined with an empty value.
  • -I β€” -Idirectory or -I directory adds the specified directory to the search path for include files. The directory may be an absolute path or a relative path to the current working directory. πŸ—‚οΈ

Code Generation Options πŸ’…

  • -g β€” Requests that the compiler place source-level debug information into the object code, such as identifier names and line numbers.

This option restrains -O from turning on the strip-debug-info optimization pass. πŸ’–

Note Currently this option has no effect. Full functionality depends on glslang support for generating debug info.
  • -O0, -Os β€” -O specifies which optimization level to use:
    • -O0 means "no optimization". This level generates the most debuggable code.
    • -O means the default optimization level for better performance.
    • -Os enables optimizations to reduce code size.
  • -mfmt=<format> β€” selects output format for compilation output in SPIR-V binary code form. Supported options are listed in the binary output format options table. This option is only valid to be used when the compilation output is SPIR-V binary code. Specifying any options listed below when the output is not SPIR-V binary code, like disassembly (with -S specified), text (with -M, -MM or -E specified) will trigger an error. 🚨

Table 2. Binary Output Format Options

  • bin β€” Output SPIR-V binary code as a sequence of binary 32-bit words in host native endianness. This is the default format for SPIR-V binary compilation output.
  • num β€” Output SPIR-V binary code as a text file containing a list of comma-separated hex numbers. Example: glslc -c -mfmt=num main.vert -o output_file.txt Content of the output_file.txt:
0x07230203,0x00010000,0x00080001,0x00000006...
  • c β€” Output SPIR-V binary code as a text file containing C-style initializer list. This is just wrapping the output of num option with curly brackets. Example: glslc -c -mfmt=c main.vert -o output_file.txt Content of output_file.txt:
{0x07230203, 0x00010000, 0x00080001, 0x00000006...}
  • -fhlsl-16bit-types β€” Enables 16bit types for HLSL compilation. πŸ”₯
  • -fhlsl-offsets β€” Use HLSL packing rules instead of GLSL rules when determining offsets of members of blocks. This option is always on when compiling for HLSL.
  • -fhlsl-functionality1 β€” Enable extension SPV_GOOGLE_hlsl_functionality1, and instructs the compiler to:
    • Annotate HLSL semantic string decorations on interface objects
    • Explicitly record the association of a UAV resource with its companion counter buffer.

This option can also be spelled with an underscore: -fhlsl_functionality1. ✨

  • -fentry-point=<name> β€” lets you specify the entry point name. This is only significant for HLSL compilation. The default is "main".
  • -fauto-map-locations β€” For GLSL compilation, option -fauto-map-locations directs the compiler to automatically assign location numbers to user-defined stage input and output variables if not explicitly specified by the shader source.

For HLSL compilation, this option is on by default.

Client APIs normally require adjacent stages to agree on their I/O interface. The compiler only sees one stage at a time, so it is strongly recommended that you avoid relying on this option to assign locations. πŸ‘‘

Instead, an explicit binding number should be specified in the shader source, as follows:

  • In a GLSL shader, use a location layout qualifier:
layout(location = 1) in vec4 x;
  • In an HLSL shader, use a vk::location attribute:
[[vk::location(1)]] float4 FooShader(
  [[vk::location(0)]] float4 a,
  [[vk::location(2)]] float4 b) : COLOR0 {
  return a + b;
}
  • -fpreserve-bindings β€” Directs the optimizer to preserve bindings declarations, even when those bindings are known to be unused. 🫢

Warning and Error Options ⚠️

  • -w β€” suppresses all warning output from glslc. Any warning that would have been generated is silently ignored. 🀫
  • -Werror β€” forces any warning to be treated as an error in glslc. This means that all warning: messages are shown as error: and any warnings will cause a non-zero exit code from glslc. If -w is specified the warnings generated are suppressed before they are converted to errors.

Dependency Generation Options πŸ“

  • -M or -MM β€” -M generates make dependencies. It outputs a rule suitable for make describing the dependencies of the input file. Instead of outputting the result of preprocessing, the preprocessor outputs one make rule containing the SPIR-V object file name for that source file, a colon, and the names of all the included files.

Unless specified explicitly (with -MT), the SPIR-V object file name in the generated make rules follows the rules of Output File Naming as in -c compilation stage.

Specifying -M implies -E, and suppresses warnings with an implicit -w. By default the output will be written to stdout, unless -MF or -o is specified.

The dependency info file name can be specified by -o and -MF options. When both are specified, the -o option is ignored. 🚫

Specifying multiple input files is valid when the make rules are written to stdout, which means neither -MF nor -o is specified. When -o or -MF is specified, only one input file is allowed.

-MM is an alias for -M.

E.g., glslc -M main.vert will dump main.vert.spv: main.vert <other included files> to stdout. More examples are listed in Dependency Generation Examples. πŸ’œ

  • -MD β€” bestie, this one is the multitasker πŸ’… it tells the glslc compiler to do TWO things at once: compile your source and generate make dependencies. The dependency info goes into a file whose name is figured out like this: if you specified the -MF option, then girl, it uses its argument. Otherwise? It just takes the name of the file holding the compilation results and slaps .d on the end. ✨

Heads up though: specifying multiple input files is totes valid only when neither -MF nor -o is specified. The second you bring -o or -MF into the chat, only one input file is allowed. no cap.

For example, glslc -c -MD main.vert will generate main.vert.spv as the SPIR-V object file and main.vert.spv.d as the dependency info file. There are so many more examples down in Dependency Generation Examples if you wanna see them all. 🫢

  • -MF β€” this cutie lets you specify the dependency info file's name when it's used with -M or -MD. But real talk: this option is invalid when used with multiple input files, okay? Don't do it. πŸ™ˆ

For example, glslc -c -MD main.vert -MF dep_info will generate main.vert.spv as the SPIR-V object file and dep_info as the dependency info file. iconic.

  • -MT β€” okay so this one lets you specify the target of the rule emitted by dependency generation, when it's used with -M or -MD. And same energy as -MF: it's invalid when used with multiple input files. periodt.

For example, glslc -M main.vert -MT target will dump the following dependency info to stdout: target: main.vert <other dependent files>. πŸ¦‹

🌈 Table 3. Dependency Generation Examples 🌈

Every single combo, served fresh, no filtering πŸ’– (columns are: Command Line Input / Compilation Output File / Dependency Output File / Dependency Info):

  • glslc -M main.vert
    • Compilation Output File: <NA>
    • Dependency Output File: <Stdout>
    • Dependency Info: main.vert.spv: main.vert
  • glslc -M a.vert b.vert
    • Compilation Output File: <NA>
    • Dependency Output File: <Stdout>
    • Dependency Info: a.vert.spv: a.vert
  • glslc -M a.vert b.vert (second output, same command 🫢)
    • Compilation Output File: <NA>
    • Dependency Output File: <Stdout>
    • Dependency Info: b.vert.spv: b.vert
  • glslc -M main.vert -o dep_info
    • Compilation Output File: <NA>
    • Dependency Output File: dep_info
    • Dependency Info: main.vert.spv: main.vert
  • glslc -M main.vert -MF dep_info
    • Compilation Output File: <NA>
    • Dependency Output File: dep_info
    • Dependency Info: main.vert.spv: main.vert
  • glslc -M main.vert -MT target
    • Compilation Output File: <NA>
    • Dependency Output File: <Stdout>
    • Dependency Info: target: main.vert
  • glslc -MD main.vert
    • Compilation Output File: a.spv
    • Dependency Output File: main.vert.spv.d
    • Dependency Info: main.vert.spv: main.vert
  • glslc -c -MD main.vert
    • Compilation Output File: main.vert.spv
    • Dependency Output File: main.vert.spv.d
    • Dependency Info: main.vert.spv: main.vert
  • glslc -c -MD a.vert b.vert
    • Compilation Output File: a.vert.spv
    • Dependency Output File: a.vert.spv.d
    • Dependency Info: a.vert.spv: a.vert
  • glslc -c -MD a.vert b.vert (second output, same command πŸ’…)
    • Compilation Output File: b.vert.spv
    • Dependency Output File: b.vert.spv.d
    • Dependency Info: b.vert.spv: b.vert
  • glslc -S -MD main.vert
    • Compilation Output File: main.vert.spvasm
    • Dependency Output File: main.vert.spvasm.d
    • Dependency Info: main.vert.spvasm: main.vert
  • glslc -c -MD main.vert -MF dep_info
    • Compilation Output File: main.vert.spv
    • Dependency Output File: dep_info
    • Dependency Info: main.vert.spv: main.vert
  • glslc -c -MD main.vert -o obj
    • Compilation Output File: obj
    • Dependency Output File: obj.d
    • Dependency Info: obj: main.vert
  • glslc -c -MD main.vert -o obj -MF dep_info -MT target
    • Compilation Output File: obj
    • Dependency Output File: dep_info
    • Dependency Info: target: main.vert

πŸŽ€ Resource Binding Options πŸŽ€

  • -fauto-bind-uniforms β€” okay this option directs the compiler to automatically assign binding numbers to uniform variables, but ONLY when an explicit binding isn't specified in the shader source. πŸ’–

Wanna be explicit instead? You can specify a binding number in the shader source using a binding layout qualifier. Like, for example:

layout(binding = 12) uniform texture2D;
  • -fhlsl-iomap β€” this one directs the compiler to use HLSL register assignments as binding values. slay. ✨
  • -fimage-binding-base β€” -fimage-binding-base [stage] base sets the lowest automatically assigned binding for images. If a stage is specified, it only affects that specified stage, bestie.

For HLSL, it sets one less than the base. πŸ‘‘

  • -fsampler-binding-base β€” -fsampler-binding-base [stage] base sets the lowest automatically assigned binding for samplers. If a stage is specified, it only affects that specified stage. πŸ¦‹

For HLSL, it sets one less than the base.

  • -ftexture-binding-base β€” -ftexture-binding-base [stage] base sets the lowest automatically assigned binding for textures. If a stage is specified, it only affects that specified stage. 🌸

For HLSL, it sets one less than the base.

  • -fubo-binding-base β€” -fubo-binding-base [stage] base sets the lowest automatically assigned binding for Uniform Buffer Objects (GLSL) or Cbuffers (HLSL). If a stage is specified, it only affects that specified stage. πŸ’œ

For HLSL, it sets one less than the base.

  • -fcbuffer-binding-base β€” this one is literally the same energy as -fubo-binding-base [stage] base. Like, identical twin behaviour, no notes. πŸ’…
  • -fssbo-binding-base β€” sets the lowest auto-assigned binding for Shader Storage Buffer Objects (that's the GLSL ones, bestie). If you drop a [stage] in there, only that stage gets the glow-up. ✨

Heads up: this one only affects GLSL compilation. That's it, that's the whole vibe.

  • -fuav-binding-base β€” sets one less than the lowest automatically assigned binding for Unordered Access Views (UAVs). Yep, one less β€” that's not a typo, that's the spec, don't come for me πŸ™ˆ. Same deal with the optional [stage]: name a stage and only that stage is affected.

And this one only affects HLSL compilation. Different lane, same energy. πŸ¦‹

  • -fresource-set-binding β€” ok so -fresource-set-binding [stage] <reg0> <set0> <binding0> sets the descriptor set and binding for an HLSL resource, and you do it by register name. Want more registers? Just keep appending triples β€” register name, descriptor set, binding. Infinite stacking, we love to see it. πŸ‘‘

Example time:

# For a texture in register t1, use set 1 binding 0.
# For a texture in register t2, use set 1 binding 3
glslc -x hlsl foo.frag -fresource-set-binding t1 1 0 t2 1 3

And if you specify a stage, it only affects that stage. Periodt.

# Same as the previous example, but the settings only apply
# to fragment (pixel) shaders.
glslc -x hlsl foo.frag -fresource-set-binding frag t1 1 0 t2 1 3

🌈 DIVERGENCE FROM AND EXTENSIONS TO GLSL SPECIFICATIONS 🌈

✨ Source-filename-based #line and __FILE__ ✨

Okay so this section is all about how the glslc compiler extends the syntax for the #line directive and the __FILE__ macro. By default β€” like, out of the box, no flags needed β€” glslc turns on the GL_GOOGLE_cpp_style_line_directive extension. And it'll generate this extended syntax into the preprocessed output (aka the stuff you get with -E). πŸŽ€

Warning This section is still evolving. Expect changes. (Yes bestie, it literally says that. Don't get attached. 😭)

So. GLSL specs have this whole notion of source strings. And I quote:

The source for a single shader is an array of strings of characters from the character set. A single shader is made from the concatenation of these strings. β€” Section 3.2 of both version 3.30 and 4.50

So with that framing in mind, the second parameter of the #line directive should be a constant integer expression representing the source string number. And the __FILE__ macro will "substitute a decimal integer constant that says which source string number is currently being processed." Straight from the spec, no cap. πŸ’œ

Now here's where glslc does its thing: it implements the standard #line and __FILE__ syntax, and it gives you an extension β€” GL_GOOGLE_cpp_style_line_directive β€” so you can use actual source filenames instead of those integer source string indices. Iconic behaviour honestly. πŸ‘‘

So specifically, the #line directive can have β€” after macro substitution β€” one of these three forms:

#line line-number
#line line-number integer-source-string-index
#line line-number "source-filename"

where source-filename can be any combination of characters except double quotation marks. (And just so you know, per the GLSL specification: "there are no escape sequences or other uses of the backslash beyond use as the line-continuation character". So no sneaky escapes, bestie. 🚫)

And if you're using source-filename-based #line, then __FILE__ expands to a string whose contents are the filename, quoted with double quotation marks. That filename is determined as the last of:

  • The filename given to the glslc compiler,
  • The filename argument of the most recent #line directive, if any.

πŸ¦‹ #include πŸ¦‹

So glslc extends GLSL with include syntax by turning on the GL_GOOGLE_include_directive extension. It'll preprocess and substitute #include directives properly, with the following behaviours. πŸ’…

Warning This section is still evolving. Expect changes. (Again. It's giving "work in progress" and we respect it. 🌸)

If #include directives show up in a shader, there will be an #extension GL_GOOGLE_include_directive : enable line generated into the preprocessed output. Little paper trail, love that for us.

Also: the GL_GOOGLE_cpp_style_line_directive extension is implicitly turned on by the GL_GOOGLE_include_directive extension. Package deal. 🎁

Now the rules, and listen closely because these matter:

  • The file argument to #include must be enclosed in double quotes. 🫢
  • It must be a relative path, using whatever path separator your OS supports.
  • However, the last path element β€” like, the name of the file itself β€” must not contain either / or \, regardless of which path separator is used. And here's the spicy part: this will not be flagged as an error. It will instead trigger undefined behavior. πŸ’€
  • For example, say there's a file named f\ilename.vert on a Unix OS. It is not possible to craft a #include that includes that file. Sorry bestie, that file is unreachable. 😭
  • Furthermore, it is not possible to escape any characters in a #include directive, so the file argument cannot contain any special characters that need escaping in C.

And finally, the actual lookup vibe: the file argument is a relative path that's matched first against the including file's own directory, and then against all -I arguments in order of their appearance on the command line. If the file can't be found, glslc aborts with an error. No file, no service. 🚫


And that's the whole page, bestie!! πŸŽ‰ You just read the entire GLSLC(1) manual page, main character energy fr fr. This one's the GLSLC(1) page, dated 2026-07-19, and yeah β€” the source/version and that date live in the footer like a lil signature. Screenshot it, cite it, whatever. Slay. πŸ’–βœ¨πŸ‘‘

πŸ“¦ where this page comes from