How GL_KHR_vulkan_glsl Targets Vulkan in GLSL

This article explains how the GL_KHR_vulkan_glsl preprocessor macro functions within the OpenGL Shading Language (GLSL) to target the Vulkan graphics API. When compiling shaders to SPIR-V for Vulkan, compilers automatically define this macro to enable Vulkan-specific syntax, semantics, and resource models while disabling legacy OpenGL constructs. Understanding this macro allows developers to write dual-compatible shader codebases that adapt cleanly between OpenGL and Vulkan environments.

The Role of the Preprocessor Macro

When a shader compiler such as glslangValidator or glslc is configured to target Vulkan (typically via the -V or --target-env=vulkan flags), it implicitly sets the GL_KHR_vulkan_glsl preprocessor macro to an integer value of 100. In addition to this macro, compilers commonly define the standard shorthand macro VULKAN with the value 100.

The presence of GL_KHR_vulkan_glsl informs the compilation pipeline that the shader must adhere to the rules defined in the GL_KHR_vulkan_glsl extension specification rather than traditional OpenGL GLSL specifications.

Feature Adjustments and Vulkan Syntax

Defining this macro changes how the compiler parses resources and keywords:

  • Descriptor Sets: It enables the set layout qualifier, allowing constructs such as layout(set = 0, binding = 0) uniform buffer, which are fundamental to Vulkan’s descriptor set model but absent in core OpenGL.
  • Push Constants: It activates the layout(push_constant) layout identifier, enabling memory blocks to receive push constant data directly from Vulkan command buffers.
  • Subpass Inputs: It provides access to subpass input types (such as subpassInput and subpassInputMS) alongside their associated fetch functions for reading directly from attachments in a render pass.
  • Combined vs. Separate Samplers: It supports explicit separation of textures and samplers (texture2D and sampler) via Vulkan SPIR-V capabilities, distinct from combined image-samplers (sampler2D).
  • Removal of Incompatible Features: The compiler disables OpenGL-only conventions, such as default-block uniform variables without explicit bindings, atomic counter types, and specific fixed-function inputs.

Conditional Compilation Example

Developers leverage the macro to write shaders that compile seamlessly for both standard OpenGL and Vulkan targets using conditional compilation blocks:

#version 450

#if defined(GL_KHR_vulkan_glsl)
    // Vulkan-specific resource binding
    layout(set = 0, binding = 0) uniform CameraData {
        mat4 viewProjection;
    } camera;
#else
    // Standard OpenGL resource binding
    layout(binding = 0) uniform CameraData {
        mat4 viewProjection;
    } camera;
#endif

void main() {
    // Shared shader logic
}

By querying #if defined(GL_KHR_vulkan_glsl) or #if defined(VULKAN), the preprocessor selectively includes API-specific qualifiers before the code is transformed into the final intermediate representation or driver-level assembly.