Home | History | Annotate | Download | only in src
      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 #include "shaderc_private.h"
     16 
     17 #include <algorithm>
     18 #include <cassert>
     19 #include <cstdint>
     20 #include <sstream>
     21 #include <vector>
     22 
     23 #include "SPIRV/spirv.hpp"
     24 
     25 #include "libshaderc_util/compiler.h"
     26 #include "libshaderc_util/counting_includer.h"
     27 #include "libshaderc_util/resources.h"
     28 #include "libshaderc_util/spirv_tools_wrapper.h"
     29 #include "libshaderc_util/version_profile.h"
     30 
     31 #if (defined(_MSC_VER) && !defined(_CPPUNWIND)) || !defined(__EXCEPTIONS)
     32 #define TRY_IF_EXCEPTIONS_ENABLED
     33 #define CATCH_IF_EXCEPTIONS_ENABLED(X) if (0)
     34 #else
     35 #define TRY_IF_EXCEPTIONS_ENABLED try
     36 #define CATCH_IF_EXCEPTIONS_ENABLED(X) catch (X)
     37 #endif
     38 
     39 namespace {
     40 
     41 // Returns shader stage (ie: vertex, fragment, etc.) in response to forced
     42 // shader kinds. If the shader kind is not a forced kind, returns EshLangCount
     43 // to let #pragma annotation or shader stage deducer determine the stage to
     44 // use.
     45 EShLanguage GetForcedStage(shaderc_shader_kind kind) {
     46   switch (kind) {
     47     case shaderc_glsl_vertex_shader:
     48       return EShLangVertex;
     49     case shaderc_glsl_fragment_shader:
     50       return EShLangFragment;
     51     case shaderc_glsl_compute_shader:
     52       return EShLangCompute;
     53     case shaderc_glsl_geometry_shader:
     54       return EShLangGeometry;
     55     case shaderc_glsl_tess_control_shader:
     56       return EShLangTessControl;
     57     case shaderc_glsl_tess_evaluation_shader:
     58       return EShLangTessEvaluation;
     59     case shaderc_glsl_infer_from_source:
     60     case shaderc_glsl_default_vertex_shader:
     61     case shaderc_glsl_default_fragment_shader:
     62     case shaderc_glsl_default_compute_shader:
     63     case shaderc_glsl_default_geometry_shader:
     64     case shaderc_glsl_default_tess_control_shader:
     65     case shaderc_glsl_default_tess_evaluation_shader:
     66     case shaderc_spirv_assembly:
     67       return EShLangCount;
     68   }
     69   assert(0 && "Unhandled shaderc_shader_kind");
     70   return EShLangCount;
     71 }
     72 
     73 // Converts shaderc_target_env to EShMessages
     74 EShMessages GetMessageRules(shaderc_target_env target) {
     75   switch (target) {
     76     case shaderc_target_env_opengl_compat:
     77       break;
     78     case shaderc_target_env_opengl:
     79       return static_cast<EShMessages>(EShMsgSpvRules | EShMsgCascadingErrors);
     80     case shaderc_target_env_vulkan:
     81       return static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules |
     82                                       EShMsgCascadingErrors);
     83   }
     84   return EShMsgCascadingErrors;
     85 }
     86 
     87 // A wrapper functor class to be used as stage deducer for libshaderc_util
     88 // Compile() interface. When the given shader kind is one of the default shader
     89 // kinds, this functor will be called if #pragma is not found in the source
     90 // code. And it returns the corresponding shader stage. When the shader kind is
     91 // a forced shader kind, this functor won't be called and it simply returns
     92 // EShLangCount to make the syntax correct. When the shader kind is set to
     93 // shaderc_glsl_deduce_from_pragma, this functor also returns EShLangCount, but
     94 // the compiler should emit error if #pragma annotation is not found in this
     95 // case.
     96 class StageDeducer {
     97  public:
     98   explicit StageDeducer(
     99       shaderc_shader_kind kind = shaderc_glsl_infer_from_source)
    100       : kind_(kind), error_(false){};
    101   // The method that underlying glslang will call to determine the shader stage
    102   // to be used in current compilation. It is called only when there is neither
    103   // forced shader kind (or say stage, in the view of glslang), nor #pragma
    104   // annotation in the source code. This method transforms an user defined
    105   // 'default' shader kind to the corresponding shader stage. As this is the
    106   // last trial to determine the shader stage, failing to find the corresponding
    107   // shader stage will record an error.
    108   // Note that calling this method more than once during one compilation will
    109   // have the error recorded for the previous call been overwriten by the next
    110   // call.
    111   EShLanguage operator()(std::ostream* /*error_stream*/,
    112                          const shaderc_util::string_piece& /*error_tag*/) {
    113     EShLanguage stage = GetDefaultStage(kind_);
    114     if (stage == EShLangCount) {
    115       error_ = true;
    116     } else {
    117       error_ = false;
    118     }
    119     return stage;
    120   };
    121 
    122   // Returns true if there is error during shader stage deduction.
    123   bool error() const { return error_; }
    124 
    125  private:
    126   // Gets the corresponding shader stage for a given 'default' shader kind. All
    127   // other kinds are mapped to EShLangCount which should not be used.
    128   EShLanguage GetDefaultStage(shaderc_shader_kind kind) const {
    129     switch (kind) {
    130       case shaderc_glsl_vertex_shader:
    131       case shaderc_glsl_fragment_shader:
    132       case shaderc_glsl_compute_shader:
    133       case shaderc_glsl_geometry_shader:
    134       case shaderc_glsl_tess_control_shader:
    135       case shaderc_glsl_tess_evaluation_shader:
    136       case shaderc_glsl_infer_from_source:
    137         return EShLangCount;
    138       case shaderc_glsl_default_vertex_shader:
    139         return EShLangVertex;
    140       case shaderc_glsl_default_fragment_shader:
    141         return EShLangFragment;
    142       case shaderc_glsl_default_compute_shader:
    143         return EShLangCompute;
    144       case shaderc_glsl_default_geometry_shader:
    145         return EShLangGeometry;
    146       case shaderc_glsl_default_tess_control_shader:
    147         return EShLangTessControl;
    148       case shaderc_glsl_default_tess_evaluation_shader:
    149         return EShLangTessEvaluation;
    150       case shaderc_spirv_assembly:
    151         return EShLangCount;
    152     }
    153     assert(0 && "Unhandled shaderc_shader_kind");
    154     return EShLangCount;
    155   }
    156 
    157   shaderc_shader_kind kind_;
    158   bool error_;
    159 };
    160 
    161 // A bridge between the libshaderc includer and libshaderc_util includer.
    162 class InternalFileIncluder : public shaderc_util::CountingIncluder {
    163  public:
    164   InternalFileIncluder(const shaderc_include_resolve_fn resolver,
    165                        const shaderc_include_result_release_fn result_releaser,
    166                        void* user_data)
    167       : resolver_(resolver),
    168         result_releaser_(result_releaser),
    169         user_data_(user_data){};
    170   InternalFileIncluder()
    171       : resolver_(nullptr), result_releaser_(nullptr), user_data_(nullptr){};
    172 
    173  private:
    174   // Check the validity of the callbacks.
    175   bool AreValidCallbacks() const {
    176     return resolver_ != nullptr && result_releaser_ != nullptr;
    177   }
    178 
    179   // Maps a shaderc_include_type to the correpsonding Glslang include type.
    180   shaderc_include_type GetIncludeType(
    181       glslang::TShader::Includer::IncludeType type) {
    182     switch (type) {
    183       case glslang::TShader::Includer::EIncludeRelative:
    184         return shaderc_include_type_relative;
    185       case glslang::TShader::Includer::EIncludeStandard:
    186         return shaderc_include_type_standard;
    187       default:
    188         break;
    189     }
    190     assert(0 && "Unhandled shaderc_include_type");
    191     return shaderc_include_type_relative;
    192   }
    193 
    194   // Resolves an include request for the requested source of the given
    195   // type in the context of the specified requesting source.  On success,
    196   // returns a newly allocated IncludeResponse containing the fully resolved
    197   // name of the requested source and the contents of that source.
    198   // On failure, returns a newly allocated IncludeResponse where the
    199   // resolved name member is an empty string, and the contents members
    200   // contains error details.
    201   virtual glslang::TShader::Includer::IncludeResult* include_delegate(
    202       const char* requested_source,
    203       glslang::TShader::Includer::IncludeType type,
    204       const char* requesting_source, size_t include_depth) override {
    205     if (!AreValidCallbacks()) {
    206       const char kUnexpectedIncludeError[] =
    207           "#error unexpected include directive";
    208       return new glslang::TShader::Includer::IncludeResult{
    209           "", kUnexpectedIncludeError, strlen(kUnexpectedIncludeError),
    210           nullptr};
    211     }
    212     shaderc_include_result* include_result =
    213         resolver_(user_data_, requested_source, GetIncludeType(type),
    214                   requesting_source, include_depth);
    215     // Make a glslang IncludeResult from a shaderc_include_result.  The
    216     // user_data member of the IncludeResult is a pointer to the
    217     // shaderc_include_result object, so we can later release the latter.
    218     return new glslang::TShader::Includer::IncludeResult{
    219         std::string(include_result->source_name,
    220                     include_result->source_name_length),
    221         include_result->content, include_result->content_length,
    222         include_result};
    223   }
    224 
    225   // Releases the given IncludeResult.
    226   virtual void release_delegate(
    227       glslang::TShader::Includer::IncludeResult* result) override {
    228     if (result && result_releaser_) {
    229       result_releaser_(user_data_,
    230                        static_cast<shaderc_include_result*>(result->user_data));
    231     }
    232     delete result;
    233   }
    234 
    235   const shaderc_include_resolve_fn resolver_;
    236   const shaderc_include_result_release_fn result_releaser_;
    237   void* user_data_;
    238 };
    239 
    240 }  // anonymous namespace
    241 
    242 struct shaderc_compile_options {
    243   shaderc_compile_options() : include_user_data(nullptr) {}
    244   shaderc_util::Compiler compiler;
    245   shaderc_include_resolve_fn include_resolver;
    246   shaderc_include_result_release_fn include_result_releaser;
    247   void* include_user_data;
    248 };
    249 
    250 shaderc_compile_options_t shaderc_compile_options_initialize() {
    251   return new (std::nothrow) shaderc_compile_options;
    252 }
    253 
    254 shaderc_compile_options_t shaderc_compile_options_clone(
    255     const shaderc_compile_options_t options) {
    256   if (!options) {
    257     return shaderc_compile_options_initialize();
    258   }
    259   return new (std::nothrow) shaderc_compile_options(*options);
    260 }
    261 
    262 void shaderc_compile_options_release(shaderc_compile_options_t options) {
    263   delete options;
    264 }
    265 
    266 void shaderc_compile_options_add_macro_definition(
    267     shaderc_compile_options_t options, const char* name, size_t name_length,
    268     const char* value, size_t value_length) {
    269   options->compiler.AddMacroDefinition(name, name_length, value, value_length);
    270 }
    271 
    272 void shaderc_compile_options_set_generate_debug_info(
    273     shaderc_compile_options_t options) {
    274   options->compiler.SetGenerateDebugInfo();
    275 }
    276 
    277 void shaderc_compile_options_set_forced_version_profile(
    278     shaderc_compile_options_t options, int version, shaderc_profile profile) {
    279   // Transfer the profile parameter from public enum type to glslang internal
    280   // enum type. No default case here so that compiler will complain if new enum
    281   // member is added later but not handled here.
    282   switch (profile) {
    283     case shaderc_profile_none:
    284       options->compiler.SetForcedVersionProfile(version, ENoProfile);
    285       break;
    286     case shaderc_profile_core:
    287       options->compiler.SetForcedVersionProfile(version, ECoreProfile);
    288       break;
    289     case shaderc_profile_compatibility:
    290       options->compiler.SetForcedVersionProfile(version, ECompatibilityProfile);
    291       break;
    292     case shaderc_profile_es:
    293       options->compiler.SetForcedVersionProfile(version, EEsProfile);
    294       break;
    295   }
    296 }
    297 
    298 void shaderc_compile_options_set_include_callbacks(
    299     shaderc_compile_options_t options, shaderc_include_resolve_fn resolver,
    300     shaderc_include_result_release_fn result_releaser, void* user_data) {
    301   options->include_resolver = resolver;
    302   options->include_result_releaser = result_releaser;
    303   options->include_user_data = user_data;
    304 }
    305 
    306 void shaderc_compile_options_set_suppress_warnings(
    307     shaderc_compile_options_t options) {
    308   options->compiler.SetSuppressWarnings();
    309 }
    310 
    311 void shaderc_compile_options_set_target_env(shaderc_compile_options_t options,
    312                                             shaderc_target_env target,
    313                                             uint32_t version) {
    314   // "version" reserved for future use, intended to distinguish between
    315   // different versions of a target environment
    316   options->compiler.SetMessageRules(GetMessageRules(target));
    317 }
    318 
    319 void shaderc_compile_options_set_warnings_as_errors(
    320     shaderc_compile_options_t options) {
    321   options->compiler.SetWarningsAsErrors();
    322 }
    323 
    324 shaderc_compiler_t shaderc_compiler_initialize() {
    325   static shaderc_util::GlslInitializer* initializer =
    326       new shaderc_util::GlslInitializer;
    327   shaderc_compiler_t compiler = new (std::nothrow) shaderc_compiler;
    328   compiler->initializer = initializer;
    329   return compiler;
    330 }
    331 
    332 void shaderc_compiler_release(shaderc_compiler_t compiler) { delete compiler; }
    333 
    334 namespace {
    335 shaderc_compilation_result_t CompileToSpecifiedOutputType(
    336     const shaderc_compiler_t compiler, const char* source_text,
    337     size_t source_text_size, shaderc_shader_kind shader_kind,
    338     const char* input_file_name, const char* entry_point_name,
    339     const shaderc_compile_options_t additional_options,
    340     shaderc_util::Compiler::OutputType output_type) {
    341   auto* result = new (std::nothrow) shaderc_compilation_result_vector;
    342   if (!result) return nullptr;
    343 
    344   if (!input_file_name) {
    345     result->messages = "Input file name string was null.";
    346     result->num_errors = 1;
    347     result->compilation_status = shaderc_compilation_status_compilation_error;
    348     return result;
    349   }
    350   result->compilation_status = shaderc_compilation_status_invalid_stage;
    351   bool compilation_succeeded = false;  // In case we exit early.
    352   std::vector<uint32_t> compilation_output_data;
    353   size_t compilation_output_data_size_in_bytes = 0u;
    354   if (!compiler->initializer) return result;
    355   TRY_IF_EXCEPTIONS_ENABLED {
    356     std::stringstream errors;
    357     size_t total_warnings = 0;
    358     size_t total_errors = 0;
    359     std::string input_file_name_str(input_file_name);
    360     EShLanguage forced_stage = GetForcedStage(shader_kind);
    361     shaderc_util::string_piece source_string =
    362         shaderc_util::string_piece(source_text, source_text + source_text_size);
    363     StageDeducer stage_deducer(shader_kind);
    364     if (additional_options) {
    365       InternalFileIncluder includer(additional_options->include_resolver,
    366                                     additional_options->include_result_releaser,
    367                                     additional_options->include_user_data);
    368       // Depends on return value optimization to avoid extra copy.
    369       std::tie(compilation_succeeded, compilation_output_data,
    370                compilation_output_data_size_in_bytes) =
    371           additional_options->compiler.Compile(
    372               source_string, forced_stage, input_file_name_str,
    373               // stage_deducer has a flag: error_, which we need to check later.
    374               // We need to make this a reference wrapper, so that std::function
    375               // won't make a copy for this callable object.
    376               std::ref(stage_deducer), includer, output_type, &errors,
    377               &total_warnings, &total_errors, compiler->initializer);
    378     } else {
    379       // Compile with default options.
    380       InternalFileIncluder includer;
    381       std::tie(compilation_succeeded, compilation_output_data,
    382                compilation_output_data_size_in_bytes) =
    383           shaderc_util::Compiler().Compile(
    384               source_string, forced_stage, input_file_name_str,
    385               std::ref(stage_deducer), includer, output_type, &errors,
    386               &total_warnings, &total_errors, compiler->initializer);
    387     }
    388 
    389     result->messages = errors.str();
    390     result->SetOutputData(std::move(compilation_output_data));
    391     result->output_data_size = compilation_output_data_size_in_bytes;
    392     result->num_warnings = total_warnings;
    393     result->num_errors = total_errors;
    394     if (compilation_succeeded) {
    395       result->compilation_status = shaderc_compilation_status_success;
    396     } else {
    397       // Check whether the error is caused by failing to deduce the shader
    398       // stage. If it is the case, set the error type to shader kind error.
    399       // Otherwise, set it to compilation error.
    400       result->compilation_status =
    401           stage_deducer.error() ? shaderc_compilation_status_invalid_stage
    402                                 : shaderc_compilation_status_compilation_error;
    403     }
    404   }
    405   CATCH_IF_EXCEPTIONS_ENABLED(...) {
    406     result->compilation_status = shaderc_compilation_status_internal_error;
    407   }
    408   return result;
    409 }
    410 }  // anonymous namespace
    411 
    412 shaderc_compilation_result_t shaderc_compile_into_spv(
    413     const shaderc_compiler_t compiler, const char* source_text,
    414     size_t source_text_size, shaderc_shader_kind shader_kind,
    415     const char* input_file_name, const char* entry_point_name,
    416     const shaderc_compile_options_t additional_options) {
    417   return CompileToSpecifiedOutputType(
    418       compiler, source_text, source_text_size, shader_kind, input_file_name,
    419       entry_point_name, additional_options,
    420       shaderc_util::Compiler::OutputType::SpirvBinary);
    421 }
    422 
    423 shaderc_compilation_result_t shaderc_compile_into_spv_assembly(
    424     const shaderc_compiler_t compiler, const char* source_text,
    425     size_t source_text_size, shaderc_shader_kind shader_kind,
    426     const char* input_file_name, const char* entry_point_name,
    427     const shaderc_compile_options_t additional_options) {
    428   return CompileToSpecifiedOutputType(
    429       compiler, source_text, source_text_size, shader_kind, input_file_name,
    430       entry_point_name, additional_options,
    431       shaderc_util::Compiler::OutputType::SpirvAssemblyText);
    432 }
    433 
    434 shaderc_compilation_result_t shaderc_compile_into_preprocessed_text(
    435     const shaderc_compiler_t compiler, const char* source_text,
    436     size_t source_text_size, shaderc_shader_kind shader_kind,
    437     const char* input_file_name, const char* entry_point_name,
    438     const shaderc_compile_options_t additional_options) {
    439   return CompileToSpecifiedOutputType(
    440       compiler, source_text, source_text_size, shader_kind, input_file_name,
    441       entry_point_name, additional_options,
    442       shaderc_util::Compiler::OutputType::PreprocessedText);
    443 }
    444 
    445 shaderc_compilation_result_t shaderc_assemble_into_spv(
    446     const shaderc_compiler_t compiler, const char* source_assembly,
    447     size_t source_assembly_size) {
    448   auto* result = new (std::nothrow) shaderc_compilation_result_spv_binary;
    449   if (!result) return nullptr;
    450   result->compilation_status = shaderc_compilation_status_invalid_assembly;
    451   if (!compiler->initializer) return result;
    452   if (source_assembly == nullptr) return result;
    453 
    454   TRY_IF_EXCEPTIONS_ENABLED {
    455     spv_binary assembling_output_data = nullptr;
    456     std::string errors;
    457     const bool assembling_succeeded = shaderc_util::SpirvToolsAssemble(
    458         {source_assembly, source_assembly + source_assembly_size},
    459         &assembling_output_data, &errors);
    460     result->num_errors = !assembling_succeeded;
    461     if (assembling_succeeded) {
    462       result->SetOutputData(assembling_output_data);
    463       result->output_data_size =
    464           assembling_output_data->wordCount * sizeof(uint32_t);
    465       result->compilation_status = shaderc_compilation_status_success;
    466     } else {
    467       result->messages = std::move(errors);
    468       result->compilation_status = shaderc_compilation_status_invalid_assembly;
    469     }
    470   }
    471   CATCH_IF_EXCEPTIONS_ENABLED(...) {
    472     result->compilation_status = shaderc_compilation_status_internal_error;
    473   }
    474 
    475   return result;
    476 }
    477 
    478 size_t shaderc_result_get_length(const shaderc_compilation_result_t result) {
    479   return result->output_data_size;
    480 }
    481 
    482 size_t shaderc_result_get_num_warnings(
    483     const shaderc_compilation_result_t result) {
    484   return result->num_warnings;
    485 }
    486 
    487 size_t shaderc_result_get_num_errors(
    488     const shaderc_compilation_result_t result) {
    489   return result->num_errors;
    490 }
    491 
    492 const char* shaderc_result_get_bytes(
    493     const shaderc_compilation_result_t result) {
    494   return result->GetBytes();
    495 }
    496 
    497 void shaderc_result_release(shaderc_compilation_result_t result) {
    498   delete result;
    499 }
    500 
    501 const char* shaderc_result_get_error_message(
    502     const shaderc_compilation_result_t result) {
    503   return result->messages.c_str();
    504 }
    505 
    506 shaderc_compilation_status shaderc_result_get_compilation_status(
    507     const shaderc_compilation_result_t result) {
    508   return result->compilation_status;
    509 }
    510 
    511 void shaderc_get_spv_version(unsigned int* version, unsigned int* revision) {
    512   *version = spv::Version;
    513   *revision = spv::Revision;
    514 }
    515 
    516 bool shaderc_parse_version_profile(const char* str, int* version,
    517                                    shaderc_profile* profile) {
    518   EProfile glslang_profile;
    519   bool success = shaderc_util::ParseVersionProfile(
    520       std::string(str, strlen(str)), version, &glslang_profile);
    521   if (!success) return false;
    522 
    523   switch (glslang_profile) {
    524     case EEsProfile:
    525       *profile = shaderc_profile_es;
    526       return true;
    527     case ECoreProfile:
    528       *profile = shaderc_profile_core;
    529       return true;
    530     case ECompatibilityProfile:
    531       *profile = shaderc_profile_compatibility;
    532       return true;
    533     case ENoProfile:
    534       *profile = shaderc_profile_none;
    535       return true;
    536     case EBadProfile:
    537       return false;
    538   }
    539 
    540   // Shouldn't reach here, all profile enum should be handled above.
    541   // Be strict to return false.
    542   return false;
    543 }
    544