blob: 298344ebc2c62de4df231d3fb3912bbc67bdf90d [file] [log] [blame]
// Copyright 2015 The Shaderc Authors. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <array>
#include <cassert>
#include <functional>
#include <ostream>
#include <string>
#include <unordered_map>
#include <utility>
#include "glslang/Public/ShaderLang.h"
#include "counting_includer.h"
#include "file_finder.h"
#include "mutex.h"
#include "resources.h"
#include "string_piece.h"
// Fix a typo in glslang/Public/ShaderLang.h
#define EShTargetClientVersion EshTargetClientVersion
namespace shaderc_util {
// To break recursive including. This header is already included in
// spirv_tools_wrapper.h, so cannot include spirv_tools_wrapper.h here.
enum class PassId;
// Initializes glslang on creation, and destroys it on completion.
// This object is expected to be a singleton, so that internal
// glslang state can be correctly handled.
// TODO(awoloszyn): Once glslang no longer has static global mutable state
// remove this class.
class GlslangInitializer {
GlslangInitializer() { glslang::InitializeProcess(); }
~GlslangInitializer() { glslang::FinalizeProcess(); }
// Calls release on GlslangInitializer used to intialize this object
// when it is destroyed.
class InitializationToken {
~InitializationToken() {
if (initializer_) {
InitializationToken(InitializationToken&& other)
: initializer_(other.initializer_) {
other.initializer_ = nullptr;
InitializationToken(const InitializationToken&) = delete;
InitializationToken(GlslangInitializer* initializer)
: initializer_(initializer) {}
friend class GlslangInitializer;
GlslangInitializer* initializer_;
// Obtains exclusive access to the glslang state. The state remains
// exclusive until the Initialization Token has been destroyed.
InitializationToken Acquire() {
return InitializationToken(this);
void Release() { state_lock_.unlock(); }
friend class InitializationToken;
mutex state_lock_;
// Maps macro names to their definitions. Stores string_pieces, so the
// underlying strings must outlive it.
using MacroDictionary = std::unordered_map<std::string, std::string>;
// Holds all of the state required to compile source GLSL into SPIR-V.
class Compiler {
// Source language
enum class SourceLanguage {
GLSL, // The default
// Target environment.
enum class TargetEnv {
Vulkan, // Default to Vulkan 1.0
OpenGL, // Default to OpenGL 4.5
OpenGLCompat, // Deprecated.
// Target environment versions. These numbers match those used by Glslang.
enum class TargetEnvVersion : uint32_t {
// For Vulkan, use numbering scheme from vulkan.h
Vulkan_1_0 = ((1 << 22)), // Default to Vulkan 1.0
Vulkan_1_1 = ((1 << 22) | (1 << 12)), // Default to Vulkan 1.0
// For OpenGL, use the numbering from #version in shaders.
OpenGL_4_5 = 450,
enum class OutputType {
SpirvBinary, // A binary module, as defined by the SPIR-V specification.
SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project.
PreprocessedText, // Preprocessed source code.
// Supported optimization levels.
enum class OptimizationLevel {
Zero, // No optimization.
Size, // Optimization towards reducing code size.
Performance, // Optimization towards better performance.
// Resource limits. These map to the "max*" fields in glslang::TBuiltInResource.
enum class Limit {
#include ""
// Types of uniform variables.
enum class UniformKind {
// Image, and image buffer.
Image = 0,
// Pure sampler.
Sampler = 1,
// Sampled texture in GLSL.
// Shader Resource View, for HLSL. (Read-only image or storage buffer.)
Texture = 2,
// Uniform Buffer Object, or UBO, in GLSL.
// Also a Cbuffer in HLSL.
Buffer = 3,
// Shader Storage Buffer Object, or SSBO
StorageBuffer = 4,
// Uniform Access View, in HLSL. (Writable storage image or storage
// buffer.)
UnorderedAccessView = 5,
enum { kNumUniformKinds = int(UniformKind::UnorderedAccessView) + 1 };
// Shader pipeline stage.
// TODO(dneto): Replaces interface uses of EShLanguage with this enum.
enum class Stage {
enum { kNumStages = int(Stage::Compute) + 1 };
// Returns a std::array of all the Stage values.
const std::array<Stage, kNumStages>& stages() const {
static std::array<Stage, kNumStages> values{
{Stage::Vertex, Stage::TessEval, Stage::TessControl, Stage::Geometry,
Stage::Fragment, Stage::Compute}};
return values;
// Creates an default compiler instance targeting at Vulkan environment. Uses
// version 110 and no profile specification as the default for GLSL.
// The default version for glsl is 110, or 100 if you are using an es
// profile. But we want to default to a non-es profile.
: default_version_(110),
target_env_version_(0), // Resolve default later.
hlsl_explicit_bindings_() {}
// Requests that the compiler place debug information into the object code,
// such as identifier names and line numbers.
void SetGenerateDebugInfo();
// Sets the optimization level to the given level. Only the last one takes
// effect if multiple calls of this method exist.
void SetOptimizationLevel(OptimizationLevel level);
// Enables or disables HLSL legalization passes.
void EnableHlslLegalization(bool hlsl_legalization_enabled);
// Enables or disables extension SPV_GOOGLE_hlsl_functionality1
void EnableHlslFunctionality1(bool enable);
// When a warning is encountered it treat it as an error.
void SetWarningsAsErrors();
// Any warning message generated is suppressed before it is output.
void SetSuppressWarnings();
// Adds an implicit macro definition obeyed by subsequent CompileShader()
// calls. The macro and definition should be passed in with their char*
// pointer and their lengths. They can be modified or deleted after this
// function has returned.
void AddMacroDefinition(const char* macro, size_t macro_length,
const char* definition, size_t definition_length);
// Sets the target environment, including version. The version value should
// be 0 or one of the values from TargetEnvVersion. The 0 version value maps
// to Vulkan 1.0 if the target environment is Vulkan, and it maps to OpenGL
// 4.5 if the target environment is OpenGL.
void SetTargetEnv(TargetEnv env, uint32_t version = 0);
// Sets the souce language.
void SetSourceLanguage(SourceLanguage lang);
// Forces (without any verification) the default version and profile for
// subsequent CompileShader() calls.
void SetForcedVersionProfile(int version, EProfile profile);
// Sets a resource limit.
void SetLimit(Limit limit, int value);
// Returns the current limit.
int GetLimit(Limit limit) const;
// Set whether the compiler automatically assigns bindings to
// uniform variables that don't have explicit bindings.
void SetAutoBindUniforms(bool auto_bind) { auto_bind_uniforms_ = auto_bind; }
// Sets the lowest binding number used when automatically assigning bindings
// for uniform resources of the given type, for all shader stages. The default
// base is zero.
void SetAutoBindingBase(UniformKind kind, uint32_t base) {
for (auto stage : stages()) {
SetAutoBindingBaseForStage(stage, kind, base);
// Sets the lowest binding number used when automatically assigning bindings
// for uniform resources of the given type for a specific shader stage. The
// default base is zero.
void SetAutoBindingBaseForStage(Stage stage, UniformKind kind,
uint32_t base) {
auto_binding_base_[static_cast<int>(stage)][static_cast<int>(kind)] = base;
// Sets whether the compiler automatically assigns locations to
// uniform variables that don't have explicit locations.
void SetAutoMapLocations(bool auto_map) { auto_map_locations_ = auto_map; }
// Use HLSL IO mapping rules for bindings. Default is false.
void SetHlslIoMapping(bool hlsl_iomap) { hlsl_iomap_ = hlsl_iomap; }
// Use HLSL rules for offsets in "transparent" memory. These allow for
// tighter packing of some combinations of types than standard GLSL packings.
void SetHlslOffsets(bool hlsl_offsets) { hlsl_offsets_ = hlsl_offsets; }
// Sets an explicit set and binding for the given HLSL register.
void SetHlslRegisterSetAndBinding(const std::string& reg,
const std::string& set,
const std::string& binding) {
for (auto stage : stages()) {
SetHlslRegisterSetAndBindingForStage(stage, reg, set, binding);
// Sets an explicit set and binding for the given HLSL register in the given
// shader stage. For example,
// SetHlslRegisterSetAndBinding(Stage::Fragment, "t1", "4", "5")
// means register "t1" in a fragment shader should map to binding 5 in set 4.
// (Glslang wants this data as strings, not ints or enums.) The string data is
// copied.
void SetHlslRegisterSetAndBindingForStage(Stage stage, const std::string& reg,
const std::string& set,
const std::string& binding) {
// Compiles the shader source in the input_source_string parameter.
// If the forced_shader stage parameter is not EShLangCount then
// the shader is assumed to be of the given stage.
// For HLSL compilation, entry_point_name is the null-terminated string for
// the entry point. For GLSL compilation, entry_point_name is ignored, and
// compilation assumes the entry point is named "main".
// The stage_callback function will be called if a shader_stage has
// not been forced and the stage can not be determined
// from the shader text. Any #include directives are parsed with the given
// includer.
// The initializer parameter must be a valid GlslangInitializer object.
// Acquire will be called on the initializer and the result will be
// destroyed before the function ends.
// The output_type parameter determines what kind of output should be
// produced.
// Any error messages are written as if the file name were error_tag.
// Any errors are written to the error_stream parameter.
// total_warnings and total_errors are incremented once for every
// warning or error encountered respectively.
// Returns a tuple consisting of three fields. 1) a boolean which is true when
// the compilation succeeded, and false otherwise; 2) a vector of 32-bit words
// which contains the compilation output data, either compiled SPIR-V binary
// code, or the text string generated in preprocessing-only or disassembly
// mode; 3) the size of the output data in bytes. When the output is SPIR-V
// binary code, the size is the number of bytes of valid data in the vector.
// If the output is a text string, the size equals the length of that string.
std::tuple<bool, std::vector<uint32_t>, size_t> Compile(
const string_piece& input_source_string, EShLanguage forced_shader_stage,
const std::string& error_tag, const char* entry_point_name,
const std::function<EShLanguage(std::ostream* error_stream,
const string_piece& error_tag)>&
CountingIncluder& includer, OutputType output_type,
std::ostream* error_stream, size_t* total_warnings, size_t* total_errors,
GlslangInitializer* initializer) const;
static EShMessages GetDefaultRules() {
return static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules |
// Preprocesses a shader whose filename is filename and content is
// shader_source. If preprocessing is successful, returns true, the
// preprocessed shader, and any warning message as a tuple. Otherwise,
// returns false, an empty string, and error messages as a tuple.
// The error_tag parameter is the name to use for outputting errors.
// The shader_source parameter is the input shader's source text.
// The shader_preamble parameter is a context-specific preamble internally
// prepended to shader_text without affecting the validity of its #version
// position.
// Any #include directives are processed with the given includer.
// If force_version_profile_ is set, the shader's version/profile is forced
// to be default_version_/default_profile_ regardless of the #version
// directive in the source code.
std::tuple<bool, std::string, std::string> PreprocessShader(
const std::string& error_tag, const string_piece& shader_source,
const string_piece& shader_preamble, CountingIncluder& includer) const;
// Cleans up the preamble in a given preprocessed shader.
// The error_tag parameter is the name to be given for the main file.
// The pound_extension parameter is the #extension directive we prepended to
// the original shader source code via preamble.
// The num_include_directives parameter is the number of #include directives
// appearing in the original shader source code.
// The is_for_next_line means whether the #line sets the line number for the
// next line.
// If no #include directive is used in the shader source code, we can safely
// delete the #extension directive we injected via preamble. Otherwise, we
// need to adjust it if there exists a #version directive in the original
// shader source code.
std::string CleanupPreamble(const string_piece& preprocessed_shader,
const string_piece& error_tag,
const string_piece& pound_extension,
int num_include_directives,
bool is_for_next_line) const;
// Determines version and profile from command line, or the source code.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> DeduceVersionProfile(
const std::string& preprocessed_shader) const;
// Determines the shader stage from pragmas embedded in the source text if
// possible. In the returned pair, the glslang EShLanguage is the shader
// stage deduced. If no #pragma directives for shader stage exist, it's
// EShLangCount. If errors occur, the second element in the pair is the
// error message. Otherwise, it's an empty string.
std::pair<EShLanguage, std::string> GetShaderStageFromSourceCode(
string_piece filename, const std::string& preprocessed_shader) const;
// Determines version and profile from command line, or the source code.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> DeduceVersionProfile(
const std::string& preprocessed_shader);
// Gets version and profile specification from the given preprocessedshader.
// Returns the decoded version and profile pair on success. Otherwise,
// returns (0, ENoProfile).
std::pair<int, EProfile> GetVersionProfileFromSourceCode(
const std::string& preprocessed_shader) const;
// Version to use when force_version_profile_ is true.
int default_version_;
// Profile to use when force_version_profile_ is true.
EProfile default_profile_;
// When true, use the default version and profile from eponymous data members.
bool force_version_profile_;
// Macro definitions that must be available to reference in the shader source.
MacroDictionary predefined_macros_;
// When true, treat warnings as errors.
bool warnings_as_errors_;
// Supress warnings when true.
bool suppress_warnings_;
// When true, compilation will generate debug info with the binary SPIR-V
// output.
bool generate_debug_info_;
// Optimization passes to be applied.
std::vector<PassId> enabled_opt_passes_;
// The target environment to compile with. This controls the glslang
// EshMessages bitmask, which determines which dialect of GLSL and which
// SPIR-V codegen semantics are used. This impacts the warning & error
// messages as well as the set of available builtins, as per the
// implementation of glslang.
TargetEnv target_env_;
// The version number of the target environment. The numbering scheme is
// particular to each target environment. If this is 0, then use a default
// for that particular target environment. See libshaders/shaderc/shaderc.h
// for those defaults.
uint32_t target_env_version_;
// The source language. Defaults to GLSL.
SourceLanguage source_language_;
// The resource limits to be used.
TBuiltInResource limits_;
// True if the compiler should automatically bind uniforms that don't
// have explicit bindings.
bool auto_bind_uniforms_;
// The base binding number per uniform type, per stage, used when automatically
// binding uniforms that don't hzve explicit bindings in the shader source.
// The default is zero.
uint32_t auto_binding_base_[kNumStages][kNumUniformKinds];
// True if the compiler should automatically map uniforms that don't
// have explicit locations.
bool auto_map_locations_;
// True if the compiler should use HLSL IO mapping rules when compiling HLSL.
bool hlsl_iomap_;
// True if the compiler should determine block member offsets using HLSL
// packing rules instead of standard GLSL rules.
bool hlsl_offsets_;
// True if the compiler should perform legalization optimization passes if
// source language is HLSL.
bool hlsl_legalization_enabled_;
// True if the compiler should support extension SPV_GOOGLE_hlsl_functionality1.
bool hlsl_functionality1_enabled_;
// A sequence of triples, each triple representing a specific HLSL register
// name, and the set and binding numbers it should be mapped to, but in
// the form of strings. This is how Glslang wants to consume the data.
std::vector<std::string> hlsl_explicit_bindings_[kNumStages];
// Converts a string to a vector of uint32_t by copying the content of a given
// string to the vector and returns it. Appends '\0' at the end if extra bytes
// are required to complete the last element.
std::vector<uint32_t> ConvertStringToVector(const std::string& str);
// Converts a valid Glslang shader stage value to a Compiler::Stage value.
inline Compiler::Stage ConvertToStage(EShLanguage stage) {
switch (stage) {
case EShLangVertex:
return Compiler::Stage::Vertex;
case EShLangTessControl:
return Compiler::Stage::TessEval;
case EShLangTessEvaluation:
return Compiler::Stage::TessControl;
case EShLangGeometry:
return Compiler::Stage::Geometry;
case EShLangFragment:
return Compiler::Stage::Fragment;
case EShLangCompute:
return Compiler::Stage::Compute;
assert(false && "Invalid case");
return Compiler::Stage::Compute;
} // namespace shaderc_util