text: // 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
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #ifndef SHADERC_SHADERC_HPP_
  #define SHADERC_SHADERC_HPP_
  
  #include <memory>
  #include <string>
  #include <vector>
  
  #include "shaderc.h"
  
  namespace shaderc {
  // A CompilationResult contains the compiler output, compilation status,
  // and messages.
  //
  // The compiler output is stored as an array of elements and accessed
  // via random access iterators provided by cbegin() and cend().  The iterators
  // are contiguous in the sense of "Contiguous Iterators: A Refinement of
  // Random Access Iterators", Nevin Liber, C++ Library Evolution Working
  // Group Working Paper N3884.
  // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3884.pdf
  //
  // Methods begin() and end() are also provided to enable range-based for.
  // They are synonyms to cbegin() and cend(), respectively.
  template <typename OutputElementType>
  class CompilationResult {
   public:
    typedef OutputElementType element_type;
    // The type used to describe the begin and end iterators on the
    // compiler output.
    typedef const OutputElementType* const_iterator;
  
    // Upon creation, the CompilationResult takes ownership of the
    // shaderc_compilation_result instance. During destruction of the
    // CompilationResult, the shaderc_compilation_result will be released.
    explicit CompilationResult(shaderc_compilation_result_t compilation_result)
        : compilation_result_(compilation_result) {}
    CompilationResult() : compilation_result_(nullptr) {}
    ~CompilationResult() { shaderc_result_release(compilation_result_); }
  
    CompilationResult(CompilationResult&& other) : compilation_result_(nullptr) {
      *this = std::move(other);
    }
  
    CompilationResult& operator=(CompilationResult&& other) {
      if (compilation_result_) {
        shaderc_result_release(compilation_result_);
      }
      compilation_result_ = other.compilation_result_;
      other.compilation_result_ = nullptr;
      return *this;
    }
  
    // Returns any error message found during compilation.
    std::string GetErrorMessage() const {
      if (!compilation_result_) {
        return "";
      }
      return shaderc_result_get_error_message(compilation_result_);
    }
  
    // Returns the compilation status, indicating whether the compilation
    // succeeded, or failed due to some reasons, like invalid shader stage or
    // compilation errors.
    shaderc_compilation_status GetCompilationStatus() const {
      if (!compilation_result_) {
        return shaderc_compilation_status_null_result_object;
      }
      return shaderc_result_get_compilation_status(compilation_result_);
    }
  
    // Returns a random access (contiguous) iterator pointing to the start
    // of the compilation output.  It is valid for the lifetime of this object.
    // If there is no compilation result, then returns nullptr.
    const_iterator cbegin() const {
      if (!compilation_result_) return nullptr;
      return reinterpret_cast<const_iterator>(
          shaderc_result_get_bytes(compilation_result_));
    }
  
    // Returns a random access (contiguous) iterator pointing to the end of
    // the compilation output.  It is valid for the lifetime of this object.
    // If there is no compilation result, then returns nullptr.
    const_iterator cend() const {
      if (!compilation_result_) return nullptr;
      return cbegin() +
             shaderc_result_get_length(compilation_result_) /
                 sizeof(OutputElementType);
    }
  
    // Returns the same iterator as cbegin().
    const_iterator begin() const { return cbegin(); }
    // Returns the same iterator as cend().
    const_iterator end() const { return cend(); }
  
    // Returns the number of warnings generated during the compilation.
    size_t GetNumWarnings() const {
      if (!compilation_result_) {
        return 0;
      }
      return shaderc_result_get_num_warnings(compilation_result_);
    }
  
    // Returns the number of errors generated during the compilation.
    size_t GetNumErrors() const {
      if (!compilation_result_) {
        return 0;
      }
      return shaderc_result_get_num_errors(compilation_result_);
    }
  
   private:
    CompilationResult(const CompilationResult& other) = delete;
    CompilationResult& operator=(const CompilationResult& other) = delete;
  
    shaderc_compilation_result_t compilation_result_;
  };
  
  // A compilation result for a SPIR-V binary module, which is an array
  // of uint32_t words.
  using SpvCompilationResult = CompilationResult<uint32_t>;
  // A compilation result in SPIR-V assembly syntax.
  using AssemblyCompilationResult = CompilationResult<char>;
  // Preprocessed source text.
  using PreprocessedSourceCompilationResult = CompilationResult<char>;
  
  // Contains any options that can have default values for a compilation.
  class CompileOptions {
   public:
    CompileOptions() { options_ = shaderc_compile_options_initialize(); }
    ~CompileOptions() { shaderc_compile_options_release(options_); }
    CompileOptions(const CompileOptions& other) {
      options_ = shaderc_compile_options_clone(other.options_);
    }
    CompileOptions(CompileOptions&& other) {
      options_ = other.options_;
      other.options_ = nullptr;
    }
  
    // Adds a predefined macro to the compilation options. It behaves the same as
    // shaderc_compile_options_add_macro_definition in shaderc.h.
    void AddMacroDefinition(const char* name, size_t name_length,
                            const char* value, size_t value_length) {
      shaderc_compile_options_add_macro_definition(options_, name, name_length,
                                                   value, value_length);
    }
  
    // Adds a valueless predefined macro to the compilation options.
    void AddMacroDefinition(const std::string& name) {
      AddMacroDefinition(name.c_str(), name.size(), nullptr, 0u);
    }
  
    // Adds a predefined macro to the compilation options.
    void AddMacroDefinition(const std::string& name, const std::string& value) {
      AddMacroDefinition(name.c_str(), name.size(), value.c_str(), value.size());
    }
  
    // Sets the compiler mode to generate debug information in the output.
    void SetGenerateDebugInfo() {
      shaderc_compile_options_set_generate_debug_info(options_);
    }
  
    // Sets the compiler optimization level to the given level. Only the last one
    // takes effect if multiple calls of this function exist.
    void SetOptimizationLevel(shaderc_optimization_level level) {
      shaderc_compile_options_set_optimization_level(options_, level);
    }
  
    // A C++ version of the libshaderc includer interface.
    class IncluderInterface {
     public:
      // Handles shaderc_include_resolver_fn callbacks.
      virtual shaderc_include_result* GetInclude(const char* requested_source,
                                                 shaderc_include_type type,
                                                 const char* requesting_source,
                                                 size_t include_depth) = 0;
  
      // Handles shaderc_include_result_release_fn callbacks.
      virtual void ReleaseInclude(shaderc_include_result* data) = 0;
  
      virtual ~IncluderInterface() = default;
    };
  
    // Sets the includer instance for libshaderc to call during compilation, as
    // described in shaderc_compile_options_set_include_callbacks().  Callbacks
    // are routed to this includer's methods.
    void SetIncluder(std::unique_ptr<IncluderInterface>&& includer) {
      includer_ = std::move(includer);
      shaderc_compile_options_set_include_callbacks(
          options_,
          [](void* user_data, const char* requested_source, int type,
             const char* requesting_source, size_t include_depth) {
            auto* sub_includer = static_cast<IncluderInterface*>(user_data);
            return sub_includer->GetInclude(requested_source,
                                        (shaderc_include_type)type,
                                        requesting_source, include_depth);
          },
          [](void* user_data, shaderc_include_result* include_result) {
            auto* sub_includer = static_cast<IncluderInterface*>(user_data);
            return sub_includer->ReleaseInclude(include_result);
          },
          includer_.get());
    }
  
    // Forces the GLSL language version and profile to a given pair. The version
    // number is the same as would appear in the #version annotation in the
    // source. Version and profile specified here overrides the #version
    // annotation in the source. Use profile: 'shaderc_profile_none' for GLSL
    // versions that do not define profiles, e.g. versions below 150.
    void SetForcedVersionProfile(int version, shaderc_profile profile) {
      shaderc_compile_options_set_forced_version_profile(options_, version,
                                                         profile);
    }
  
    // Sets the compiler mode to suppress warnings. Note this option overrides
    // warnings-as-errors mode. When both suppress-warnings and warnings-as-errors
    // modes are turned on, warning messages will be inhibited, and will not be
    // emitted as error message.
    void SetSuppressWarnings() {
      shaderc_compile_options_set_suppress_warnings(options_);
    }
  
    // Sets the source language. The default is GLSL.
    void SetSourceLanguage(shaderc_source_language lang) {
      shaderc_compile_options_set_source_language(options_, lang);
    }
  
    // Sets the target shader environment, affecting which warnings or errors will
    // be issued.  The version will be for distinguishing between different
    // versions of the target environment.  The version value should be either 0
    // or a value listed in shaderc_env_version.  The 0 value maps to Vulkan 1.0
    // if |target| is Vulkan, and it maps to OpenGL 4.5 if |target| is OpenGL.
    void SetTargetEnvironment(shaderc_target_env target, uint32_t version) {
      shaderc_compile_options_set_target_env(options_, target, version);
    }
  
    // Sets the target SPIR-V version.  The generated module will use this version
    // of SPIR-V.  Each target environment determines what versions of SPIR-V
    // it can consume.  Defaults to the highest version of SPIR-V 1.0 which is
    // required to be supported by the target environment.  E.g. Default to SPIR-V
    // 1.0 for Vulkan 1.0 and SPIR-V 1.3 for Vulkan 1.1.
    void SetTargetSpirv(shaderc_spirv_version version) {
      shaderc_compile_options_set_target_spirv(options_, version);
    }
  
    // Sets the compiler mode to make all warnings into errors. Note the
    // suppress-warnings mode overrides this option, i.e. if both
    // warning-as-errors and suppress-warnings modes are set on, warnings will not
    // be emitted as error message.
    void SetWarningsAsErrors() {
      shaderc_compile_options_set_warnings_as_errors(options_);
    }
  
    // Sets a resource limit.
    void SetLimit(shaderc_limit limit, int value) {
      shaderc_compile_options_set_limit(options_, limit, value);
    }
  
    // Sets whether the compiler should automatically assign bindings to uniforms
    // that aren't already explicitly bound in the shader source.
    void SetAutoBindUniforms(bool auto_bind) {
      shaderc_compile_options_set_auto_bind_uniforms(options_, auto_bind);
    }
  
    // Sets whether the compiler should use HLSL IO mapping rules for bindings.
    // Defaults to false.
    void SetHlslIoMapping(bool hlsl_iomap) {
      shaderc_compile_options_set_hlsl_io_mapping(options_, hlsl_iomap);
    }
  
    // Sets whether the compiler should determine block member offsets using HLSL
    // packing rules instead of standard GLSL rules.  Defaults to false.  Only
    // affects GLSL compilation.  HLSL rules are always used when compiling HLSL.
    void SetHlslOffsets(bool hlsl_offsets) {
      shaderc_compile_options_set_hlsl_offsets(options_, hlsl_offsets);
    }
  
    // Sets the base binding number used for for a uniform resource type when
    // automatically assigning bindings.  For GLSL compilation, sets the lowest
    // automatically assigned number.  For HLSL compilation, the regsiter number
    // assigned to the resource is added to this specified base.
    void SetBindingBase(shaderc_uniform_kind kind, uint32_t base) {
      shaderc_compile_options_set_binding_base(options_, kind, base);
    }
  
    // Like SetBindingBase, but only takes effect when compiling a given shader
    // stage.  The stage is assumed to be one of vertex, fragment, tessellation
    // evaluation, tesselation control, geometry, or compute.
    void SetBindingBaseForStage(shaderc_shader_kind shader_kind,
                                shaderc_uniform_kind kind, uint32_t base) {
      shaderc_compile_options_set_binding_base_for_stage(options_, shader_kind,
                                                         kind, base);
    }
  
    // Sets whether the compiler automatically assigns locations to
    // uniform variables that don't have explicit locations.
    void SetAutoMapLocations(bool auto_map) {
      shaderc_compile_options_set_auto_map_locations(options_, auto_map);
    }
  
    // Sets a descriptor set and binding for an HLSL register in the given stage.
    // Copies the parameter strings.
    void SetHlslRegisterSetAndBindingForStage(shaderc_shader_kind shader_kind,
                                              const std::string& reg,
                                              const std::string& set,
                                              const std::string& binding) {
      shaderc_compile_options_set_hlsl_register_set_and_binding_for_stage(
          options_, shader_kind, reg.c_str(), set.c_str(), binding.c_str());
    }
  
    // Sets a descriptor set and binding for an HLSL register in any stage.
    // Copies the parameter strings.
    void SetHlslRegisterSetAndBinding(const std::string& reg,
                                      const std::string& set,
                                      const std::string& binding) {
      shaderc_compile_options_set_hlsl_register_set_and_binding(
          options_, reg.c_str(), set.c_str(), binding.c_str());
    }
  
    // Sets whether the compiler should enable extension
    // SPV_GOOGLE_hlsl_functionality1.
    void SetHlslFunctionality1(bool enable) {
      shaderc_compile_options_set_hlsl_functionality1(options_, enable);
    }
  
    // Sets whether the compiler should invert position.Y output in vertex shader.
    void SetInvertY(bool enable) {
      shaderc_compile_options_set_invert_y(options_, enable);
    }
  
    // Sets whether the compiler should generates code for max an min which,
    // if given a NaN operand, will return the other operand. Similarly, the
    // clamp builtin will favour the non-NaN operands, as if clamp were
    // implemented as a composition of max and min.
    void SetNanClamp(bool enable) {
      shaderc_compile_options_set_nan_clamp(options_, enable);
    }
  
   private:
    CompileOptions& operator=(const CompileOptions& other) = delete;
    shaderc_compile_options_t options_;
    std::unique_ptr<IncluderInterface> includer_;
  
    friend class Compiler;
  };
  
  // The compilation context for compiling source to SPIR-V.
  class Compiler {
   public:
    Compiler() : compiler_(shaderc_compiler_initialize()) {}
    ~Compiler() { shaderc_compiler_release(compiler_); }
  
    Compiler(Compiler&& other) {
      compiler_ = other.compiler_;
      other.compiler_ = nullptr;
    }
  
    bool IsValid() const { return compiler_ != nullptr; }
  
    // Compiles the given source GLSL and returns a SPIR-V binary module
    // compilation result.
    // The source_text parameter must be a valid pointer.
    // The source_text_size parameter must be the length of the source text.
    // The shader_kind parameter either forces the compilation to be done with a
    // specified shader kind, or hint the compiler how to determine the exact
    // shader kind. If the shader kind is set to shaderc_glslc_infer_from_source,
    // the compiler will try to deduce the shader kind from the source string and
    // a failure in this proess will generate an error. Currently only #pragma
    // annotation is supported. If the shader kind is set to one of the default
    // shader kinds, the compiler will fall back to the specified default shader
    // kind in case it failed to deduce the shader kind from the source string.
    // The input_file_name is a null-termintated string. It is used as a tag to
    // identify the source string in cases like emitting error messages. It
    // doesn't have to be a 'file name'.
    // The entry_point_name parameter is a null-terminated string specifying
    // the entry point name for HLSL compilation.  For GLSL compilation, the
    // entry point name is assumed to be "main".
    // The compilation is passed any options specified in the CompileOptions
    // parameter.
    // It is valid for the returned CompilationResult object to outlive this
    // compiler object.
    // Note when the options_ has disassembly mode or preprocessing only mode set
    // on, the returned CompilationResult will hold a text string, instead of a
    // SPIR-V binary generated with default options.
    SpvCompilationResult CompileGlslToSpv(const char* source_text,
                                          size_t source_text_size,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name,
                                          const char* entry_point_name,
                                          const CompileOptions& options) const {
      shaderc_compilation_result_t compilation_result = shaderc_compile_into_spv(
          compiler_, source_text, source_text_size, shader_kind, input_file_name,
          entry_point_name, options.options_);
      return SpvCompilationResult(compilation_result);
    }
  
    // Compiles the given source shader and returns a SPIR-V binary module
    // compilation result.
    // Like the first CompileGlslToSpv method but assumes the entry point name
    // is "main".
    SpvCompilationResult CompileGlslToSpv(const char* source_text,
                                          size_t source_text_size,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name,
                                          const CompileOptions& options) const {
      return CompileGlslToSpv(source_text, source_text_size, shader_kind,
                              input_file_name, "main", options);
    }
  
    // Compiles the given source GLSL and returns a SPIR-V binary module
    // compilation result.
    // Like the previous CompileGlslToSpv method but uses default options.
    SpvCompilationResult CompileGlslToSpv(const char* source_text,
                                          size_t source_text_size,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name) const {
      shaderc_compilation_result_t compilation_result =
          shaderc_compile_into_spv(compiler_, source_text, source_text_size,
                                   shader_kind, input_file_name, "main", nullptr);
      return SpvCompilationResult(compilation_result);
    }
  
    // Compiles the given source shader and returns a SPIR-V binary module
    // compilation result.
    // Like the first CompileGlslToSpv method but the source is provided as
    // a std::string, and we assume the entry point is "main".
    SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name,
                                          const CompileOptions& options) const {
      return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
                              input_file_name, options);
    }
  
    // Compiles the given source shader and returns a SPIR-V binary module
    // compilation result.
    // Like the first CompileGlslToSpv method but the source is provided as
    // a std::string.
    SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name,
                                          const char* entry_point_name,
                                          const CompileOptions& options) const {
      return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
                              input_file_name, entry_point_name, options);
    }
  
    // Compiles the given source GLSL and returns a SPIR-V binary module
    // compilation result.
    // Like the previous CompileGlslToSpv method but assumes the entry point
    // name is "main".
    SpvCompilationResult CompileGlslToSpv(const std::string& source_text,
                                          shaderc_shader_kind shader_kind,
                                          const char* input_file_name) const {
      return CompileGlslToSpv(source_text.data(), source_text.size(), shader_kind,
                              input_file_name);
    }
  
    // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
    // compilation result.
    // The assembly should follow the syntax defined in the SPIRV-Tools project
    // (https://github.com/KhronosGroup/SPIRV-Tools/blob/master/syntax.md).
    // It is valid for the returned CompilationResult object to outlive this
    // compiler object.
    // The assembling will pick options suitable for assembling specified in the
    // CompileOptions parameter.
    SpvCompilationResult AssembleToSpv(const char* source_assembly,
                                       size_t source_assembly_size,
                                       const CompileOptions& options) const {
      return SpvCompilationResult(shaderc_assemble_into_spv(
          compiler_, source_assembly, source_assembly_size, options.options_));
    }
  
    // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
    // compilation result.
    // Like the first AssembleToSpv method but uses the default compiler options.
    SpvCompilationResult AssembleToSpv(const char* source_assembly,
                                       size_t source_assembly_size) const {
      return SpvCompilationResult(shaderc_assemble_into_spv(
          compiler_, source_assembly, source_assembly_size, nullptr));
    }
  
    // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
    // compilation result.
    // Like the first AssembleToSpv method but the source is provided as a
    // std::string.
    SpvCompilationResult AssembleToSpv(const std::string& source_assembly,
                                       const CompileOptions& options) const {
      return SpvCompilationResult(
          shaderc_assemble_into_spv(compiler_, source_assembly.data(),
                                    source_assembly.size(), options.options_));
    }
  
    // Assembles the given SPIR-V assembly and returns a SPIR-V binary module
    // compilation result.
    // Like the first AssembleToSpv method but the source is provided as a
    // std::string and also uses default compiler options.
    SpvCompilationResult AssembleToSpv(const std::string& source_assembly) const {
      return SpvCompilationResult(shaderc_assemble_into_spv(
          compiler_, source_assembly.data(), source_assembly.size(), nullptr));
    }
  
    // Compiles the given source GLSL and returns the SPIR-V assembly text
    // compilation result.
    // Options are similar to the first CompileToSpv method.
    AssemblyCompilationResult CompileGlslToSpvAssembly(
        const char* source_text, size_t source_text_size,
        shaderc_shader_kind shader_kind, const char* input_file_name,
        const char* entry_point_name, const CompileOptions& options) const {
      shaderc_compilation_result_t compilation_result =
          shaderc_compile_into_spv_assembly(
              compiler_, source_text, source_text_size, shader_kind,
              input_file_name, entry_point_name, options.options_);
      return AssemblyCompilationResult(compilation_result);
    }
  
    // Compiles the given source GLSL and returns the SPIR-V assembly text
    // compilation result.
    // Similare to the previous method, but assumes entry point name is "main".
    AssemblyCompilationResult CompileGlslToSpvAssembly(
        const char* source_text, size_t source_text_size,
        shaderc_shader_kind shader_kind, const char* input_file_name,
        const CompileOptions& options) const {
      return CompileGlslToSpvAssembly(source_text, source_text_size, shader_kind,
                                      input_file_name, "main", options);
    }
  
    // Compiles the given source GLSL and returns the SPIR-V assembly text
    // result. Like the first CompileGlslToSpvAssembly method but the source
    // is provided as a std::string.  Options are otherwise similar to
    // the first CompileToSpv method.
    AssemblyCompilationResult CompileGlslToSpvAssembly(
        const std::string& source_text, shaderc_shader_kind shader_kind,
        const char* input_file_name, const char* entry_point_name,
        const CompileOptions& options) const {
      return CompileGlslToSpvAssembly(source_text.data(), source_text.size(),
                                      shader_kind, input_file_name,
                                      entry_point_name, options);
    }
  
    // Compiles the given source GLSL and returns the SPIR-V assembly text
    // result. Like the previous  CompileGlslToSpvAssembly method but assumes
    // the entry point name is "main".
    AssemblyCompilationResult CompileGlslToSpvAssembly(
        const std::string& source_text, shaderc_shader_kind shader_kind,
        const char* input_file_name, const CompileOptions& options) const {
      return CompileGlslToSpvAssembly(source_text, shader_kind, input_file_name,
                                      "main", options);
    }
  
    // Preprocesses the given source GLSL and returns the preprocessed
    // source text as a compilation result.
    // Options are similar to the first CompileToSpv method.
    PreprocessedSourceCompilationResult PreprocessGlsl(
        const char* source_text, size_t source_text_size,
        shaderc_shader_kind shader_kind, const char* input_file_name,
        const CompileOptions& options) const {
      shaderc_compilation_result_t compilation_result =
          shaderc_compile_into_preprocessed_text(
              compiler_, source_text, source_text_size, shader_kind,
              input_file_name, "main", options.options_);
      return PreprocessedSourceCompilationResult(compilation_result);
    }
  
    // Preprocesses the given source GLSL and returns text result.  Like the first
    // PreprocessGlsl method but the source is provided as a std::string.
    // Options are otherwise similar to the first CompileToSpv method.
    PreprocessedSourceCompilationResult PreprocessGlsl(
        const std::string& source_text, shaderc_shader_kind shader_kind,
        const char* input_file_name, const CompileOptions& options) const {
      return PreprocessGlsl(source_text.data(), source_text.size(), shader_kind,
                            input_file_name, options);
    }
  
   private:
    Compiler(const Compiler&) = delete;
    Compiler& operator=(const Compiler& other) = delete;
  
    shaderc_compiler_t compiler_;
  };
  }  // namespace shaderc
  
  #endif  // SHADERC_SHADERC_HPP_
  