Home | History | Annotate | Download | only in libshaderc_util
      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