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 <array> 19 #include <cassert> 20 #include <functional> 21 #include <ostream> 22 #include <string> 23 #include <unordered_map> 24 #include <utility> 25 26 #include "glslang/Public/ShaderLang.h" 27 28 #include "counting_includer.h" 29 #include "file_finder.h" 30 #include "mutex.h" 31 #include "resources.h" 32 #include "string_piece.h" 33 34 namespace shaderc_util { 35 36 // To break recursive including. This header is already included in 37 // spirv_tools_wrapper.h, so cannot include spirv_tools_wrapper.h here. 38 enum class PassId; 39 40 // Initializes glslang on creation, and destroys it on completion. 41 // This object is expected to be a singleton, so that internal 42 // glslang state can be correctly handled. 43 // TODO(awoloszyn): Once glslang no longer has static global mutable state 44 // remove this class. 45 class GlslangInitializer { 46 public: 47 GlslangInitializer() { glslang::InitializeProcess(); } 48 49 ~GlslangInitializer() { glslang::FinalizeProcess(); } 50 51 // Calls release on GlslangInitializer used to intialize this object 52 // when it is destroyed. 53 class InitializationToken { 54 public: 55 ~InitializationToken() { 56 if (initializer_) { 57 initializer_->Release(); 58 } 59 } 60 61 InitializationToken(InitializationToken&& other) 62 : initializer_(other.initializer_) { 63 other.initializer_ = nullptr; 64 } 65 66 InitializationToken(const InitializationToken&) = delete; 67 68 private: 69 InitializationToken(GlslangInitializer* initializer) 70 : initializer_(initializer) {} 71 72 friend class GlslangInitializer; 73 GlslangInitializer* initializer_; 74 }; 75 76 // Obtains exclusive access to the glslang state. The state remains 77 // exclusive until the Initialization Token has been destroyed. 78 InitializationToken Acquire() { 79 state_lock_.lock(); 80 return InitializationToken(this); 81 } 82 83 private: 84 void Release() { state_lock_.unlock(); } 85 86 friend class InitializationToken; 87 88 mutex state_lock_; 89 }; 90 91 // Maps macro names to their definitions. Stores string_pieces, so the 92 // underlying strings must outlive it. 93 using MacroDictionary = std::unordered_map<std::string, std::string>; 94 95 // Holds all of the state required to compile source GLSL into SPIR-V. 96 class Compiler { 97 public: 98 // Source language 99 enum class SourceLanguage { 100 GLSL, // The default 101 HLSL, 102 }; 103 104 // Target environment. 105 enum class TargetEnv { 106 Vulkan, 107 OpenGL, 108 OpenGLCompat, 109 }; 110 111 enum class OutputType { 112 SpirvBinary, // A binary module, as defined by the SPIR-V specification. 113 SpirvAssemblyText, // Assembly syntax defined by the SPIRV-Tools project. 114 PreprocessedText, // Preprocessed source code. 115 }; 116 117 // Supported optimization levels. 118 enum class OptimizationLevel { 119 Zero, // No optimization. 120 Size, // Optimization towards reducing code size. 121 }; 122 123 // Resource limits. These map to the "max*" fields in glslang::TBuiltInResource. 124 enum class Limit { 125 #define RESOURCE(NAME,FIELD,CNAME) NAME, 126 #include "resources.inc" 127 #undef RESOURCE 128 }; 129 130 // Types of uniform variables. 131 enum class UniformKind { 132 // Image, and image buffer. 133 Image = 0, 134 // Pure sampler. 135 Sampler = 1, 136 // Sampled texture in GLSL. 137 // Shader Resource View, for HLSL. (Read-only image or storage buffer.) 138 Texture = 2, 139 // Uniform Buffer Object, or UBO, in GLSL. 140 // Also a Cbuffer in HLSL. 141 Buffer = 3, 142 // Shader Storage Buffer Object, or SSBO 143 StorageBuffer = 4, 144 // Uniform Access View, in HLSL. (Writable storage image or storage 145 // buffer.) 146 UnorderedAccessView = 5, 147 }; 148 enum { kNumUniformKinds = int(UniformKind::UnorderedAccessView) + 1 }; 149 150 // Shader pipeline stage. 151 // TODO(dneto): Replaces interface uses of EShLanguage with this enum. 152 enum class Stage { 153 Vertex, 154 TessEval, 155 TessControl, 156 Geometry, 157 Fragment, 158 Compute, 159 }; 160 enum { kNumStages = int(Stage::Compute) + 1 }; 161 162 // Returns a std::array of all the Stage values. 163 const std::array<Stage, kNumStages>& stages() const { 164 static std::array<Stage, kNumStages> values{ 165 {Stage::Vertex, Stage::TessEval, Stage::TessControl, Stage::Geometry, 166 Stage::Fragment, Stage::Compute}}; 167 return values; 168 } 169 170 // Creates an default compiler instance targeting at Vulkan environment. Uses 171 // version 110 and no profile specification as the default for GLSL. 172 Compiler() 173 // The default version for glsl is 110, or 100 if you are using an es 174 // profile. But we want to default to a non-es profile. 175 : default_version_(110), 176 default_profile_(ENoProfile), 177 force_version_profile_(false), 178 warnings_as_errors_(false), 179 suppress_warnings_(false), 180 generate_debug_info_(false), 181 enabled_opt_passes_(), 182 target_env_(TargetEnv::Vulkan), 183 source_language_(SourceLanguage::GLSL), 184 limits_(kDefaultTBuiltInResource), 185 auto_bind_uniforms_(false), 186 auto_binding_base_(), 187 hlsl_iomap_(false), 188 hlsl_offsets_(false), 189 hlsl_explicit_bindings_() {} 190 191 // Requests that the compiler place debug information into the object code, 192 // such as identifier names and line numbers. 193 void SetGenerateDebugInfo(); 194 195 // Sets the optimization level to the given level. Only the last one takes 196 // effect if multiple calls of this method exist. 197 void SetOptimizationLevel(OptimizationLevel level); 198 199 // When a warning is encountered it treat it as an error. 200 void SetWarningsAsErrors(); 201 202 // Any warning message generated is suppressed before it is output. 203 void SetSuppressWarnings(); 204 205 // Adds an implicit macro definition obeyed by subsequent CompileShader() 206 // calls. The macro and definition should be passed in with their char* 207 // pointer and their lengths. They can be modified or deleted after this 208 // function has returned. 209 void AddMacroDefinition(const char* macro, size_t macro_length, 210 const char* definition, size_t definition_length); 211 212 // Sets the target environment. 213 void SetTargetEnv(TargetEnv env); 214 215 // Sets the souce language. 216 void SetSourceLanguage(SourceLanguage lang); 217 218 // Forces (without any verification) the default version and profile for 219 // subsequent CompileShader() calls. 220 void SetForcedVersionProfile(int version, EProfile profile); 221 222 // Sets a resource limit. 223 void SetLimit(Limit limit, int value); 224 225 // Returns the current limit. 226 int GetLimit(Limit limit) const; 227 228 // Set whether the compiler automatically assigns bindings to 229 // uniform variables that don't have explicit bindings. 230 void SetAutoBindUniforms(bool auto_bind) { auto_bind_uniforms_ = auto_bind; } 231 232 // Sets the lowest binding number used when automatically assigning bindings 233 // for uniform resources of the given type, for all shader stages. The default 234 // base is zero. 235 void SetAutoBindingBase(UniformKind kind, uint32_t base) { 236 for (auto stage : stages()) { 237 SetAutoBindingBaseForStage(stage, kind, base); 238 } 239 } 240 241 // Sets the lowest binding number used when automatically assigning bindings 242 // for uniform resources of the given type for a specific shader stage. The 243 // default base is zero. 244 void SetAutoBindingBaseForStage(Stage stage, UniformKind kind, 245 uint32_t base) { 246 auto_binding_base_[static_cast<int>(stage)][static_cast<int>(kind)] = base; 247 } 248 249 // Use HLSL IO mapping rules for bindings. Default is false. 250 void SetHlslIoMapping(bool hlsl_iomap) { hlsl_iomap_ = hlsl_iomap; } 251 252 // Use HLSL rules for offsets in "transparent" memory. These allow for 253 // tighter packing of some combinations of types than standard GLSL packings. 254 void SetHlslOffsets(bool hlsl_offsets) { hlsl_offsets_ = hlsl_offsets; } 255 256 // Sets an explicit set and binding for the given HLSL register. 257 void SetHlslRegisterSetAndBinding(const std::string& reg, 258 const std::string& set, 259 const std::string& binding) { 260 for (auto stage : stages()) { 261 SetHlslRegisterSetAndBindingForStage(stage, reg, set, binding); 262 } 263 } 264 265 // Sets an explicit set and binding for the given HLSL register in the given 266 // shader stage. For example, 267 // SetHlslRegisterSetAndBinding(Stage::Fragment, "t1", "4", "5") 268 // means register "t1" in a fragment shader should map to binding 5 in set 4. 269 // (Glslang wants this data as strings, not ints or enums.) The string data is 270 // copied. 271 void SetHlslRegisterSetAndBindingForStage(Stage stage, const std::string& reg, 272 const std::string& set, 273 const std::string& binding) { 274 hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(reg); 275 hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(set); 276 hlsl_explicit_bindings_[static_cast<int>(stage)].push_back(binding); 277 } 278 279 // Compiles the shader source in the input_source_string parameter. 280 // 281 // If the forced_shader stage parameter is not EShLangCount then 282 // the shader is assumed to be of the given stage. 283 // 284 // For HLSL compilation, entry_point_name is the null-terminated string for 285 // the entry point. For GLSL compilation, entry_point_name is ignored, and 286 // compilation assumes the entry point is named "main". 287 // 288 // The stage_callback function will be called if a shader_stage has 289 // not been forced and the stage can not be determined 290 // from the shader text. Any #include directives are parsed with the given 291 // includer. 292 // 293 // The initializer parameter must be a valid GlslangInitializer object. 294 // Acquire will be called on the initializer and the result will be 295 // destroyed before the function ends. 296 // 297 // The output_type parameter determines what kind of output should be 298 // produced. 299 // 300 // Any error messages are written as if the file name were error_tag. 301 // Any errors are written to the error_stream parameter. 302 // total_warnings and total_errors are incremented once for every 303 // warning or error encountered respectively. 304 // 305 // Returns a tuple consisting of three fields. 1) a boolean which is true when 306 // the compilation succeeded, and false otherwise; 2) a vector of 32-bit words 307 // which contains the compilation output data, either compiled SPIR-V binary 308 // code, or the text string generated in preprocessing-only or disassembly 309 // mode; 3) the size of the output data in bytes. When the output is SPIR-V 310 // binary code, the size is the number of bytes of valid data in the vector. 311 // If the output is a text string, the size equals the length of that string. 312 std::tuple<bool, std::vector<uint32_t>, size_t> Compile( 313 const string_piece& input_source_string, EShLanguage forced_shader_stage, 314 const std::string& error_tag, const char* entry_point_name, 315 const std::function<EShLanguage(std::ostream* error_stream, 316 const string_piece& error_tag)>& 317 stage_callback, 318 CountingIncluder& includer, OutputType output_type, 319 std::ostream* error_stream, size_t* total_warnings, size_t* total_errors, 320 GlslangInitializer* initializer) const; 321 322 static EShMessages GetDefaultRules() { 323 return static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules | 324 EShMsgCascadingErrors); 325 } 326 327 protected: 328 // Preprocesses a shader whose filename is filename and content is 329 // shader_source. If preprocessing is successful, returns true, the 330 // preprocessed shader, and any warning message as a tuple. Otherwise, 331 // returns false, an empty string, and error messages as a tuple. 332 // 333 // The error_tag parameter is the name to use for outputting errors. 334 // The shader_source parameter is the input shader's source text. 335 // The shader_preamble parameter is a context-specific preamble internally 336 // prepended to shader_text without affecting the validity of its #version 337 // position. 338 // 339 // Any #include directives are processed with the given includer. 340 // 341 // If force_version_profile_ is set, the shader's version/profile is forced 342 // to be default_version_/default_profile_ regardless of the #version 343 // directive in the source code. 344 std::tuple<bool, std::string, std::string> PreprocessShader( 345 const std::string& error_tag, const string_piece& shader_source, 346 const string_piece& shader_preamble, CountingIncluder& includer) const; 347 348 // Cleans up the preamble in a given preprocessed shader. 349 // 350 // The error_tag parameter is the name to be given for the main file. 351 // The pound_extension parameter is the #extension directive we prepended to 352 // the original shader source code via preamble. 353 // The num_include_directives parameter is the number of #include directives 354 // appearing in the original shader source code. 355 // The is_for_next_line means whether the #line sets the line number for the 356 // next line. 357 // 358 // If no #include directive is used in the shader source code, we can safely 359 // delete the #extension directive we injected via preamble. Otherwise, we 360 // need to adjust it if there exists a #version directive in the original 361 // shader source code. 362 std::string CleanupPreamble(const string_piece& preprocessed_shader, 363 const string_piece& error_tag, 364 const string_piece& pound_extension, 365 int num_include_directives, 366 bool is_for_next_line) const; 367 368 // Determines version and profile from command line, or the source code. 369 // Returns the decoded version and profile pair on success. Otherwise, 370 // returns (0, ENoProfile). 371 std::pair<int, EProfile> DeduceVersionProfile( 372 const std::string& preprocessed_shader) const; 373 374 // Determines the shader stage from pragmas embedded in the source text if 375 // possible. In the returned pair, the glslang EShLanguage is the shader 376 // stage deduced. If no #pragma directives for shader stage exist, it's 377 // EShLangCount. If errors occur, the second element in the pair is the 378 // error message. Otherwise, it's an empty string. 379 std::pair<EShLanguage, std::string> GetShaderStageFromSourceCode( 380 string_piece filename, const std::string& preprocessed_shader) const; 381 382 // Determines version and profile from command line, or the source code. 383 // Returns the decoded version and profile pair on success. Otherwise, 384 // returns (0, ENoProfile). 385 std::pair<int, EProfile> DeduceVersionProfile( 386 const std::string& preprocessed_shader); 387 388 // Gets version and profile specification from the given preprocessedshader. 389 // Returns the decoded version and profile pair on success. Otherwise, 390 // returns (0, ENoProfile). 391 std::pair<int, EProfile> GetVersionProfileFromSourceCode( 392 const std::string& preprocessed_shader) const; 393 394 // Version to use when force_version_profile_ is true. 395 int default_version_; 396 // Profile to use when force_version_profile_ is true. 397 EProfile default_profile_; 398 // When true, use the default version and profile from eponymous data members. 399 bool force_version_profile_; 400 401 // Macro definitions that must be available to reference in the shader source. 402 MacroDictionary predefined_macros_; 403 404 // When true, treat warnings as errors. 405 bool warnings_as_errors_; 406 // Supress warnings when true. 407 bool suppress_warnings_; 408 409 // When true, compilation will generate debug info with the binary SPIR-V 410 // output. 411 bool generate_debug_info_; 412 413 // Optimization passes to be applied. 414 std::vector<PassId> enabled_opt_passes_; 415 416 // The target environment to compile with. This controls the glslang 417 // EshMessages bitmask, which determines which dialect of GLSL and which 418 // SPIR-V codegen semantics are used. This impacts the warning & error 419 // messages as well as the set of available builtins, as per the 420 // implementation of glslang. 421 TargetEnv target_env_; 422 423 // The source language. Defaults to GLSL. 424 SourceLanguage source_language_; 425 426 // The resource limits to be used. 427 TBuiltInResource limits_; 428 429 // True if the compiler should automatically bind uniforms that don't 430 // have explicit bindings. 431 bool auto_bind_uniforms_; 432 433 // The base binding number per uniform type, per stage, used when automatically 434 // binding uniforms that don't hzve explicit bindings in the shader source. 435 // The default is zero. 436 uint32_t auto_binding_base_[kNumStages][kNumUniformKinds]; 437 438 // True if the compiler should use HLSL IO mapping rules when compiling HLSL. 439 bool hlsl_iomap_; 440 441 // True if the compiler should determine block member offsets using HLSL 442 // packing rules instead of standard GLSL rules. 443 bool hlsl_offsets_; 444 445 // A sequence of triples, each triple representing a specific HLSL register 446 // name, and the set and binding numbers it should be mapped to, but in 447 // the form of strings. This is how Glslang wants to consume the data. 448 std::vector<std::string> hlsl_explicit_bindings_[kNumStages]; 449 }; 450 451 // Converts a string to a vector of uint32_t by copying the content of a given 452 // string to the vector and returns it. Appends '\0' at the end if extra bytes 453 // are required to complete the last element. 454 std::vector<uint32_t> ConvertStringToVector(const std::string& str); 455 456 // Converts a valid Glslang shader stage value to a Compiler::Stage value. 457 inline Compiler::Stage ConvertToStage(EShLanguage stage) { 458 switch (stage) { 459 case EShLangVertex: 460 return Compiler::Stage::Vertex; 461 case EShLangTessControl: 462 return Compiler::Stage::TessEval; 463 case EShLangTessEvaluation: 464 return Compiler::Stage::TessControl; 465 case EShLangGeometry: 466 return Compiler::Stage::Geometry; 467 case EShLangFragment: 468 return Compiler::Stage::Fragment; 469 case EShLangCompute: 470 return Compiler::Stage::Compute; 471 default: 472 break; 473 } 474 assert(false && "Invalid case"); 475 return Compiler::Stage::Compute; 476 } 477 478 } // namespace shaderc_util 479 #endif // LIBSHADERC_UTIL_INC_COMPILER_H 480