1 // Copyright 2015 The Shaderc Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef LIBSHADERC_UTIL_INC_COMPILER_H 16 #define LIBSHADERC_UTIL_INC_COMPILER_H 17 18 #include <functional> 19 #include <ostream> 20 #include <string> 21 #include <unordered_map> 22 #include <utility> 23 24 #include "glslang/Public/ShaderLang.h" 25 26 #include "counting_includer.h" 27 #include "file_finder.h" 28 #include "mutex.h" 29 #include "string_piece.h" 30 31 namespace shaderc_util { 32 33 // Initializes glslang on creation, and destroys it on completion. 34 // This object is expected to be a singleton, so that internal 35 // glslang state can be correctly handled. 36 // TODO(awoloszyn): Once glslang no longer has static global mutable state 37 // remove this class. 38 class GlslInitializer { 39 public: 40 GlslInitializer() { glslang::InitializeProcess(); } 41 42 ~GlslInitializer() { glslang::FinalizeProcess(); } 43 44 // Calls release on GlslangInitializer used to intialize this object 45 // when it is destroyed. 46 class InitializationToken { 47 public: 48 ~InitializationToken() { 49 if (initializer_) { 50 initializer_->Release(); 51 } 52 } 53 54 InitializationToken(InitializationToken&& other) 55 : initializer_(other.initializer_) { 56 other.initializer_ = nullptr; 57 } 58 59 InitializationToken(const InitializationToken&) = delete; 60 61 private: 62 InitializationToken(GlslInitializer* initializer) 63 : initializer_(initializer) {} 64 65 friend class GlslInitializer; 66 GlslInitializer* initializer_; 67 }; 68 69 // Obtains exclusive access to the glslang state. The state remains 70 // exclusive until the Initialization Token has been destroyed. 71 InitializationToken Acquire() { 72 state_lock_.lock(); 73 return InitializationToken(this); 74 } 75 76 private: 77 void Release() { state_lock_.unlock(); } 78 79 friend class InitializationToken; 80 81 mutex state_lock_; 82 }; 83 84 // Maps macro names to their definitions. Stores string_pieces, so the 85 // underlying strings must outlive it. 86 using MacroDictionary = std::unordered_map<std::string, std::string>; 87 88 // Holds all of the state required to compile source GLSL into SPIR-V. 89 class Compiler { 90 public: 91 Compiler() 92 // The default version for glsl is 110, or 100 if you are using an es 93 // profile. But we want to default to a non-es profile. 94 : default_version_(110), 95 default_profile_(ENoProfile), 96 force_version_profile_(false), 97 warnings_as_errors_(false), 98 suppress_warnings_(false), 99 generate_debug_info_(false), 100 message_rules_(GetDefaultRules()) {} 101 102 // Requests that the compiler place debug information into the object code, 103 // such as identifier names and line numbers. 104 void SetGenerateDebugInfo(); 105 106 // When a warning is encountered it treat it as an error. 107 void SetWarningsAsErrors(); 108 109 // Any warning message generated is suppressed before it is output. 110 void SetSuppressWarnings(); 111 112 // Adds an implicit macro definition obeyed by subsequent CompileShader() 113 // calls. The macro and definition should be passed in with their char* 114 // pointer and their lengths. They can be modified or deleted after this 115 // function has returned. 116 void AddMacroDefinition(const char* macro, size_t macro_length, 117 const char* definition, size_t definition_length); 118 119 // Sets message rules to be used when generating compiler warnings/errors 120 void SetMessageRules(EShMessages rules); 121 122 // Gets the message rules when generating compiler warnings/error. 123 EShMessages GetMessageRules() const; 124 125 // Forces (without any verification) the default version and profile for 126 // subsequent CompileShader() calls. 127 void SetForcedVersionProfile(int version, EProfile profile); 128 129 enum class OutputType { 130 SpirvBinary, // A binary module, as defined by the SPIR-V specification. 131 SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project. 132 PreprocessedText, // Preprocessed source code. 133 }; 134 // Compiles the shader source in the input_source_string parameter. 135 // 136 // If the forced_shader stage parameter is not EShLangCount then 137 // the shader is assumed to be of the given stage. 138 // 139 // The stage_callback function will be called if a shader_stage has 140 // not been forced and the stage can not be determined 141 // from the shader text. Any #include directives are parsed with the given 142 // includer. 143 // 144 // The initializer parameter must be a valid GlslInitializer object. 145 // Acquire will be called on the initializer and the result will be 146 // destoryed before the function ends. 147 // 148 // The output_type parameter determines what kind of output should be 149 // produced. 150 // 151 // Any error messages are written as if the file name were error_tag. 152 // Any errors are written to the error_stream parameter. 153 // total_warnings and total_errors are incremented once for every 154 // warning or error encountered respectively. 155 // 156 // Returns a tuple consisting of three fields. 1) a boolean which is true when 157 // the compilation succeeded, and false otherwise; 2) a vector of 32-bit words 158 // which contains the compilation output data, either compiled SPIR-V binary 159 // code, or the text string generated in preprocessing-only or disassembly 160 // mode; 3) the size of the output data in bytes. When the output is SPIR-V 161 // binary code, the size is the number of bytes of valid data in the vector. 162 // If the output is a text string, the size equals the length of that string. 163 std::tuple<bool, std::vector<uint32_t>, size_t> Compile( 164 const shaderc_util::string_piece& input_source_string, 165 EShLanguage forced_shader_stage, const std::string& error_tag, 166 const std::function<EShLanguage( 167 std::ostream* error_stream, 168 const shaderc_util::string_piece& error_tag)>& stage_callback, 169 CountingIncluder& includer, OutputType output_type, 170 std::ostream* error_stream, size_t* total_warnings, size_t* total_errors, 171 GlslInitializer* initializer) const; 172 173 static EShMessages GetDefaultRules() { 174 return static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules | 175 EShMsgCascadingErrors); 176 } 177 178 protected: 179 // Preprocesses a shader whose filename is filename and content is 180 // shader_source. If preprocessing is successful, returns true, the 181 // preprocessed shader, and any warning message as a tuple. Otherwise, 182 // returns false, an empty string, and error messages as a tuple. 183 // 184 // The error_tag parameter is the name to use for outputting errors. 185 // The shader_source parameter is the input shader's source text. 186 // The shader_preamble parameter is a context-specific preamble internally 187 // prepended to shader_text without affecting the validity of its #version 188 // position. 189 // 190 // Any #include directives are processed with the given includer. 191 // 192 // If force_version_profile_ is set, the shader's version/profile is forced 193 // to be default_version_/default_profile_ regardless of the #version 194 // directive in the source code. 195 std::tuple<bool, std::string, std::string> PreprocessShader( 196 const std::string& error_tag, 197 const shaderc_util::string_piece& shader_source, 198 const shaderc_util::string_piece& shader_preamble, 199 CountingIncluder& includer) const; 200 201 // Cleans up the preamble in a given preprocessed shader. 202 // 203 // The error_tag parameter is the name to be given for the main file. 204 // The pound_extension parameter is the #extension directive we prepended to 205 // the original shader source code via preamble. 206 // The num_include_directives parameter is the number of #include directives 207 // appearing in the original shader source code. 208 // The is_for_next_line means whether the #line sets the line number for the 209 // next line. 210 // 211 // If no #include directive is used in the shader source code, we can safely 212 // delete the #extension directive we injected via preamble. Otherwise, we 213 // need to adjust it if there exists a #version directive in the original 214 // shader source code. 215 std::string CleanupPreamble( 216 const shaderc_util::string_piece& preprocessed_shader, 217 const shaderc_util::string_piece& error_tag, 218 const shaderc_util::string_piece& pound_extension, 219 int num_include_directives, bool is_for_next_line) const; 220 221 // Determines version and profile from command line, or the source code. 222 // Returns the decoded version and profile pair on success. Otherwise, 223 // returns (0, ENoProfile). 224 std::pair<int, EProfile> DeduceVersionProfile( 225 const std::string& preprocessed_shader) const; 226 227 // Determines the shader stage from pragmas embedded in the source text if 228 // possible. In the returned pair, the glslang EShLanguage is the shader 229 // stage deduced. If no #pragma directives for shader stage exist, it's 230 // EShLangCount. If errors occur, the second element in the pair is the 231 // error message. Otherwise, it's an empty string. 232 std::pair<EShLanguage, std::string> GetShaderStageFromSourceCode( 233 shaderc_util::string_piece filename, 234 const std::string& preprocessed_shader) const; 235 236 // Determines version and profile from command line, or the source code. 237 // Returns the decoded version and profile pair on success. Otherwise, 238 // returns (0, ENoProfile). 239 std::pair<int, EProfile> DeduceVersionProfile( 240 const std::string& preprocessed_shader); 241 242 // Gets version and profile specification from the given preprocessedshader. 243 // Returns the decoded version and profile pair on success. Otherwise, 244 // returns (0, ENoProfile). 245 std::pair<int, EProfile> GetVersionProfileFromSourceCode( 246 const std::string& preprocessed_shader) const; 247 248 // Version to use when force_version_profile_ is true. 249 int default_version_; 250 // Profile to use when force_version_profile_ is true. 251 EProfile default_profile_; 252 // When true, use the default version and profile from eponymous data members. 253 bool force_version_profile_; 254 255 // Macro definitions that must be available to reference in the shader source. 256 MacroDictionary predefined_macros_; 257 258 // When true, treat warnings as errors. 259 bool warnings_as_errors_; 260 // Supress warnings when true. 261 bool suppress_warnings_; 262 263 // When true, compilation will generate debug info with the binary SPIR-V 264 // output. 265 bool generate_debug_info_; 266 267 // Sets the glslang EshMessages bitmask for determining which dialect of GLSL 268 // and which SPIR-V codegen semantics are used. This impacts the warning & 269 // error 270 // messages as well as the set of available builtins 271 EShMessages message_rules_; 272 }; 273 274 // Converts a string to a vector of uint32_t by copying the content of a given 275 // string to the vector and returns it. Appends '\0' at the end if extra bytes 276 // are required to complete the last element. 277 std::vector<uint32_t> ConvertStringToVector(const std::string& str); 278 279 } // namespace shaderc_util 280 #endif // LIBSHADERC_UTIL_INC_COMPILER_H 281