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