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 <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