Magnum::ShaderTools::GlslangConverter class new in Git master

Glslang shader converter plugin.

Uses Glslang for GLSL validation and GLSL to SPIR-V compilation (Format::Glsl, Format::Spirv).

This plugin provides the GlslShaderConverter and GlslToSpirvShaderConverter plugins.

Usage

This plugin depends on the ShaderTools and Glslang libraries and is built if MAGNUM_WITH_GLSLANGSHADERCONVERTER is enabled when building Magnum Plugins. To use as a dynamic plugin, load "GlslangShaderConverter" via Corrade::PluginManager::Manager.

Additionally, if you're using Magnum as a CMake subproject, bundle the magnum-plugins and glslang repositories and do the following. If you want to use system-installed Glslang, omit the first part and point CMAKE_PREFIX_PATH to their installation dir if necessary.

# Skip tests, external dependencies, glslangValidator executable, SPIR-V Tools
# integration and HLSL support, which Magnum doesn't use
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(BUILD_EXTERNAL OFF CACHE BOOL "" FORCE)
set(ENABLE_CTEST OFF CACHE BOOL "" FORCE)
set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "" FORCE)
set(ENABLE_OPT OFF CACHE BOOL "" FORCE)
set(ENABLE_HLSL OFF CACHE BOOL "" FORCE)
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "" FORCE)
# If you don't do this on version 10-11.0.0, CMake will complain that
# Glslang::Glslang depends on a non-existent path <project>/build/include in
# INTERFACE_INCLUDE_DIRECTORIES
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/include")
add_subdirectory(glslang EXCLUDE_FROM_ALL)

set(MAGNUM_WITH_GLSLANGSHADERCONVERTER ON CACHE BOOL "" FORCE)
add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL)

# So the dynamically loaded plugin gets built implicitly
add_dependencies(your-app MagnumPlugins::GlslangShaderConverter)

To use as a static plugin or as a dependency of another plugin with CMake, put FindMagnumPlugins.cmake and FindGlslang.cmake into your modules/ directory, request the GlslangShaderConverter component of the MagnumPlugins package and link to the MagnumPlugins::GlslangShaderConverter target:

find_package(MagnumPlugins REQUIRED GlslangShaderConverter)

# ...
target_link_libraries(your-app PRIVATE MagnumPlugins::GlslangShaderConverter)

See Downloading and building plugins, Plugin usage with CMake and Loading and using plugins for more information.

Compiling GLSL to SPIR-V

Use one of the convertDataToData(), convertDataToFile(), convertFileToData() or convertFileToFile() APIs to compile a GLSL source for a particular stage to SPIR-V. Only GLSL 1.40 (OpenGL 3.2) and higher is accepted by Glslang for compilation to SPIR-V, earlier versions can be only validated. See Shader stages and Input and output format and version below for details on how to specify a shader stage, input/output format version and target environment.

GLSL validation

Use validateData() or validateFile() to validate a GLSL file. Unlike SPIR-V compilation, all versions starting from GLSL 1.10 (OpenGL 2.0) can be validated. Note that in some cases, such as opening an inaccessible file or an assembly error the validation function can return {false, ""} and print a message to the error output instead.

Validation results are highly dependent on the target format and version set using setOutputFormat(), see Input and output format and version below for details. Additional validation options can be set through the plugin-specific config.

Processing #include directives

If the GL_GOOGLE_include_directive extension is enabled (which, crazily enough, isn't listed in any registry nor has any public specification), it's possible to #include other source files. You can also use the extension directive to distinguish between offline compilation and runtime compilation by a GL driver (in which case you'd add the extra sources for example with a sequence of GL::Shader::addSource() calls):

#ifdef GL_GOOGLE_include_directive
#extension GL_GOOGLE_include_directive: require
#include "fullScreenTriangle.glsl"
#include "constants.glsl"
#endif

Currently, only #include "file" directives are implemented in the plugin, system includes using #include <file> are reserved for future use.

If you validate or convert using validateFile(), convertFileToFile() or convertFileToData(), this will work automatically, with includes being searched for relative to the top-level file. If you are validating/converting data or when need more flexibility such as custom include paths, you also supply an input file callback, which will then get called for all encountered files.

While it's possible that the callback gets called multiple times for a single file due to common files being included from multiple places, a InputFileCallbackPolicy::LoadTemporary is guaranteed to be followed by a matching InputFileCallbackPolicy::Close before a InputFileCallbackPolicy::LoadTemporary happens again — or, in other words, InputFileCallbackPolicy::Close is never called twice for the same file without a corresponding InputFileCallbackPolicy::LoadTemporary in between. This means the user callbacks don't need to implement any kind of reference counting, that's handled on the plugin side.

Shader stages

When validating or converting files using validateFile(), convertFileToFile() or convertFileToData() and passing Stage::Unspecified, shader stage is detected based on filename extension suffix:

Similarly is done for filenames ending with *.<stage>.glsl. If none of above matches or if validating/converting data instead of a file, Stage::Unspecified is treated the same as Stage::Vertex.

Input and output format and version

The format passed to setInputFormat() has to be either Format::Unspecified or Format::Glsl. The GLSL version is taken from the #version directive, if present in the source, and defaults to 110 (GLSL 1.10, OpenGL 2.0) if not specified. It can be forcibly overriden with the version parameter to one of the following values, equivalently to allowed #version directives:

  • 110 for GLSL 1.10 (OpenGL 2.0)
  • 120 for GLSL 1.20 (OpenGL 2.1)
  • 130 for GLSL 1.30 (OpenGL 3.0)
  • 140 for GLSL 1.40 (OpenGL 3.1)
  • 150 for GLSL 1.50 compatibility profile (OpenGL 3.2)
  • 150 core for GLSL 1.50 core profile (OpenGL 3.2)
  • 330 for GLSL 3.30 compatibility profile (OpenGL 3.3)
  • 330 core for GLSL 3.30 core profile (OpenGL 3.3)
  • 400 for GLSL 4.00 compatibility profile (OpenGL 4.0)
  • 400 core for GLSL 4.00 core profile (OpenGL 4.0)
  • 410 for GLSL 4.10 compatibility profile (OpenGL 4.1)
  • 410 core for GLSL 4.10 core profile (OpenGL 4.1)
  • 420 for GLSL 4.20 compatibility profile (OpenGL 4.2)
  • 420 core for GLSL 4.20 core profile (OpenGL 4.2)
  • 430 for GLSL 4.30 compatibility profile (OpenGL 4.3)
  • 430 core for GLSL 4.30 core profile (OpenGL 4.3)
  • 440 for GLSL 4.40 compatibility profile (OpenGL 4.4)
  • 440 core for GLSL 4.40 core profile (OpenGL 4.4)
  • 450 for GLSL 4.50 compatibility profile (OpenGL 4.5)
  • 450 core for GLSL 4.50 core profile (OpenGL 4.5)
  • 460 for GLSL 4.60 compatibility profile (OpenGL 4.6)
  • 460 core for GLSL 4.60 core profile (OpenGL 4.6)
  • 100 es for GLSL ES 1.00 (OpenGL ES 2.0)
  • 300 es for GLSL ES 3.00 (OpenGL ES 3.0)
  • 310 es for GLSL ES 3.10 (OpenGL ES 3.1)
  • 320 es for GLSL ES 3.20 (OpenGL ES 3.2)

The format passed to setOutputFormat() has to be either Format::Unspecified or Format::Spirv. The version is divided between target and SPIR-V version, and by default targets Vulkan 1.0 and SPIR-V 1.0. You can override using the second parameter passed to setOutputFormat() either by specifying just the target, having the SPIR-V version implicit:

  • opengl for generic OpenGL without any SPIR-V rules applied (validation only)
  • opengl4.5 for OpenGL 4.5, implicitly with SPIR-V 1.0
  • vulkan1.0 for Vulkan 1.0, implicitly with SPIR-V 1.0
  • vulkan1.1 for Vulkan 1.1, implicitly with SPIR-V 1.3
  • vulkan1.2 for Vulkan 1.2, implicitly with SPIR-V 1.5

Or by specifying a <target> spv<major>.<minor> version, where <target> is one of the above and the <major>/<minor> is from the range of 1.0 to 1.5. So for example vulkan1.1 spv1.4 will target Vulkan 1.1 with SPIR-V 1.4 (instead of the default SPIR-V 1.3).

In case of validation and version set to opengl or opengl4.5, the implicit Format::Unspecified means no SPIR-V specific rules will be enforced (like explicit uniform locations) in order to make it possible to validate non-SPIR-V shaders such as GLSL ES or WebGL ones. In case of opengl4.5 you can set Format::Spirv to enforce SPIR-V rules as well, and thus behave the same as when doing a SPIR-V conversion. Using a version in the opengl4.5 spv<major>.<minor> form will also imply Format::Spirv. For Vulkan validation and SPIR-V conversion, Format::Unspecified is treated the same way as Format::Spirv.

Apart from imposing various target-specific restrictions on the GLSL source, the openglX.Y target implicitly adds #define GL_SPIRV (as specified by ARB_gl_spirv), while vulkanX.Y adds #define VULKAN (as specified by GL_KHR_vulkan_glsl). Either of those macros is always defined for conversion, for validation it's defined only if SPIR-V validation is included, as defined in the paragraph above.

Debug info level

By default, the converter outputs SPIR-V without any debug information. You can control this using setDebugInfoLevel():

  • 0 or the empty default generates no debug info
  • 1 makes the input GLSL source embedded in the OpSource instruction (including the filename, if converting from a file), together with OpLine providing line info for the instructions and OpModuleProcessed describing what all processing steps were taken by Glslang

Plugin-specific configuration

It's possible to tune various compiler and validator options through configuration(). There's also a configurable set of builtins and limits, affecting validation and compilation results. See below for all options and their default values.

[configuration]
# Additional options controlling the behavior and output in addition to
# ConverterFlag::Quiet, Verbose and WarningAsError

# Cascade errors instead of exiting after first error
cascadingErrors=true
# Be liberal in accepting input
permissive=false
# Error on use of deprecated features
forwardCompatible=false

# GLSL builtins and limits. See the following for default values:
# https://github.com/KhronosGroup/glslang/blob/master/StandAlone/ResourceLimits.cpp
[configuration/builtins]
maxLights=32
maxClipPlanes=6
maxTextureUnits=32
maxTextureCoords=32
maxVertexAttribs=64
maxVertexUniformComponents=4096
maxVaryingFloats=64
maxVertexTextureImageUnits=32
maxCombinedTextureImageUnits=80
maxTextureImageUnits=32
maxFragmentUniformComponents=4096
maxDrawBuffers=32
maxVertexUniformVectors=128
maxVaryingVectors=8
maxFragmentUniformVectors=16
maxVertexOutputVectors=16
maxFragmentInputVectors=15
minProgramTexelOffset=-8
maxProgramTexelOffset=7
maxClipDistances=8
maxComputeWorkGroupCountX=65535
maxComputeWorkGroupCountY=65535
maxComputeWorkGroupCountZ=65535
maxComputeWorkGroupSizeX=1024
maxComputeWorkGroupSizeY=1024
maxComputeWorkGroupSizeZ=64
maxComputeUniformComponents=1024
maxComputeTextureImageUnits=16
maxComputeImageUniforms=8
maxComputeAtomicCounters=8
maxComputeAtomicCounterBuffers=1
maxVaryingComponents=60
maxVertexOutputComponents=64
maxGeometryInputComponents=64
maxGeometryOutputComponents=128
maxFragmentInputComponents=128
maxImageUnits=8
maxCombinedImageUnitsAndFragmentOutputs=8
maxCombinedShaderOutputResources=8
maxImageSamples=0
maxVertexImageUniforms=0
maxTessControlImageUniforms=0
maxTessEvaluationImageUniforms=0
maxGeometryImageUniforms=0
maxFragmentImageUniforms=8
maxCombinedImageUniforms=8
maxGeometryTextureImageUnits=16
maxGeometryOutputVertices=256
maxGeometryTotalOutputComponents=1024
maxGeometryUniformComponents=1024
maxGeometryVaryingComponents=64
maxTessControlInputComponents=128
maxTessControlOutputComponents=128
maxTessControlTextureImageUnits=16
maxTessControlUniformComponents=1024
maxTessControlTotalOutputComponents=4096
maxTessEvaluationInputComponents=128
maxTessEvaluationOutputComponents=128
maxTessEvaluationTextureImageUnits=16
maxTessEvaluationUniformComponents=1024
maxTessPatchComponents=120
maxPatchVertices=32
maxTessGenLevel=64
maxViewports=16
maxVertexAtomicCounters=0
maxTessControlAtomicCounters=0
maxTessEvaluationAtomicCounters=0
maxGeometryAtomicCounters=0
maxFragmentAtomicCounters=8
maxCombinedAtomicCounters=8
maxAtomicCounterBindings=1
maxVertexAtomicCounterBuffers=0
maxTessControlAtomicCounterBuffers=0
maxTessEvaluationAtomicCounterBuffers=0
maxGeometryAtomicCounterBuffers=0
maxFragmentAtomicCounterBuffers=1
maxCombinedAtomicCounterBuffers=1
maxAtomicCounterBufferSize=16384
maxTransformFeedbackBuffers=4
maxTransformFeedbackInterleavedComponents=64
maxCullDistances=8
maxCombinedClipAndCullDistances=8
maxSamples=4
maxMeshOutputVerticesNV=256
maxMeshOutputPrimitivesNV=512
maxMeshWorkGroupSizeX_NV=32
maxMeshWorkGroupSizeY_NV=1
maxMeshWorkGroupSizeZ_NV=1
maxTaskWorkGroupSizeX_NV=32
maxTaskWorkGroupSizeY_NV=1
maxTaskWorkGroupSizeZ_NV=1
maxMeshViewCountNV=4
maxDualSourceDrawBuffersEXT=1

[configuration/limits]
nonInductiveForLoops=true
whileLoops=true
doWhileLoops=true
generalUniformIndexing=true
generalAttributeMatrixVectorIndexing=true
generalVaryingIndexing=true
generalSamplerIndexing=true
generalVariableIndexing=true
generalConstantMatrixVectorIndexing=true

See Editing plugin-specific configuration for more information and an example showing how to edit the configuration values.

Base classes

class AbstractConverter new in Git master
Base for shader converter plugins.

Public static functions

static void initialize()
Initialize the Glslang library.
static void finalize()
Finalize the Glslang library.

Constructors, destructors, conversion operators

GlslangConverter(PluginManager::AbstractManager& manager, const Containers::StringView& plugin) explicit
Plugin manager constructor.