Path: blob/main/libshaderc_util/src/compiler.cc
1560 views
// Copyright 2015 The Shaderc Authors. All rights reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314#include "libshaderc_util/compiler.h"1516#include <cstdint>17#include <iomanip>18#include <sstream>19#include <thread>20#include <tuple>2122#include "SPIRV/GlslangToSpv.h"23#include "libshaderc_util/format.h"24#include "libshaderc_util/io_shaderc.h"25#include "libshaderc_util/message.h"26#include "libshaderc_util/resources.h"27#include "libshaderc_util/shader_stage.h"28#include "libshaderc_util/spirv_tools_wrapper.h"29#include "libshaderc_util/string_piece.h"30#include "libshaderc_util/version_profile.h"31#include "spirv-tools/libspirv.hpp"3233namespace {34using shaderc_util::string_piece;3536// For use with glslang parsing calls.37const bool kNotForwardCompatible = false;3839// Returns true if #line directive sets the line number for the next line in the40// given version and profile.41inline bool LineDirectiveIsForNextLine(int version, EProfile profile) {42return profile == EEsProfile || version >= 330;43}4445// Returns a #line directive whose arguments are line and filename.46inline std::string GetLineDirective(int line, const string_piece& filename) {47return "#line " + std::to_string(line) + " \"" + filename.str() + "\"\n";48}4950// Given a canonicalized #line directive (starting exactly with "#line", using51// single spaces to separate different components, and having an optional52// newline at the end), returns the line number and string name/number. If no53// string name/number is provided, the second element in the returned pair is an54// empty string_piece. Behavior is undefined if the directive parameter is not a55// canonicalized #line directive.56std::pair<int, string_piece> DecodeLineDirective(string_piece directive) {57const string_piece kLineDirective = "#line ";58assert(directive.starts_with(kLineDirective));59directive = directive.substr(kLineDirective.size());6061const int line = std::atoi(directive.data());62const size_t space_loc = directive.find_first_of(' ');63if (space_loc == string_piece::npos) return std::make_pair(line, "");6465directive = directive.substr(space_loc);66directive = directive.strip("\" \n");67return std::make_pair(line, directive);68}6970// Returns the Glslang message rules for the given target environment,71// source language, and whether we want HLSL offset rules. We assume72// only valid combinations are used.73EShMessages GetMessageRules(shaderc_util::Compiler::TargetEnv env,74shaderc_util::Compiler::SourceLanguage lang,75bool hlsl_offsets, bool hlsl_16bit_types,76bool debug_info) {77using shaderc_util::Compiler;78EShMessages result = EShMsgCascadingErrors;79if (lang == Compiler::SourceLanguage::HLSL) {80result = static_cast<EShMessages>(result | EShMsgReadHlsl);81}82switch (env) {83case Compiler::TargetEnv::OpenGLCompat:84// The compiler will have already errored out before now.85// But we need to handle this enum.86break;87case Compiler::TargetEnv::OpenGL:88result = static_cast<EShMessages>(result | EShMsgSpvRules);89break;90case Compiler::TargetEnv::Vulkan:91result =92static_cast<EShMessages>(result | EShMsgSpvRules | EShMsgVulkanRules);93break;94}95if (hlsl_offsets) {96result = static_cast<EShMessages>(result | EShMsgHlslOffsets);97}98if (hlsl_16bit_types) {99result = static_cast<EShMessages>(result | EShMsgHlslEnable16BitTypes);100}101if (debug_info) {102result = static_cast<EShMessages>(result | EShMsgDebugInfo);103}104return result;105}106107} // anonymous namespace108109namespace shaderc_util {110111unsigned int GlslangInitializer::initialize_count_ = 0;112std::mutex* GlslangInitializer::glslang_mutex_ = nullptr;113114GlslangInitializer::GlslangInitializer() {115static std::mutex first_call_mutex;116117// If this is the first call, glslang_mutex_ needs to be created, but in118// thread safe manner.119{120const std::lock_guard<std::mutex> first_call_lock(first_call_mutex);121if (glslang_mutex_ == nullptr) {122glslang_mutex_ = new std::mutex();123}124}125126const std::lock_guard<std::mutex> glslang_lock(*glslang_mutex_);127128if (initialize_count_ == 0) {129glslang::InitializeProcess();130}131132initialize_count_++;133}134135GlslangInitializer::~GlslangInitializer() {136const std::lock_guard<std::mutex> glslang_lock(*glslang_mutex_);137138initialize_count_--;139140if (initialize_count_ == 0) {141glslang::FinalizeProcess();142// There is no delete for glslang_mutex_ here, because we cannot guarantee143// there isn't a caller waiting for glslang_mutex_ in GlslangInitializer().144//145// This means that this class does leak one std::mutex worth of memory after146// the final instance is destroyed, but this allows us to defer allocating147// and constructing until we are sure we need to.148}149}150151void Compiler::SetLimit(Compiler::Limit limit, int value) {152switch (limit) {153#define RESOURCE(NAME, FIELD, CNAME) \154case Limit::NAME: \155limits_.FIELD = value; \156break;157#include "libshaderc_util/resources.inc"158#undef RESOURCE159}160}161162int Compiler::GetLimit(Compiler::Limit limit) const {163switch (limit) {164#define RESOURCE(NAME, FIELD, CNAME) \165case Limit::NAME: \166return limits_.FIELD;167#include "libshaderc_util/resources.inc"168#undef RESOURCE169}170return 0; // Unreachable171}172173std::tuple<bool, std::vector<uint32_t>, size_t> Compiler::Compile(174const string_piece& input_source_string, EShLanguage forced_shader_stage,175const std::string& error_tag, const char* entry_point_name,176const std::function<EShLanguage(std::ostream* error_stream,177const string_piece& error_tag)>&178stage_callback,179CountingIncluder& includer, OutputType output_type,180std::ostream* error_stream, size_t* total_warnings, size_t* total_errors) const {181// Compilation results to be returned:182// Initialize the result tuple as a failed compilation. In error cases, we183// should return result_tuple directly without setting its members.184auto result_tuple =185std::make_tuple(false, std::vector<uint32_t>(), (size_t)0u);186// Get the reference of the members of the result tuple. We should set their187// values for succeeded compilation before returning the result tuple.188bool& succeeded = std::get<0>(result_tuple);189std::vector<uint32_t>& compilation_output_data = std::get<1>(result_tuple);190size_t& compilation_output_data_size_in_bytes = std::get<2>(result_tuple);191192// Check target environment.193const auto target_client_info = GetGlslangClientInfo(194error_tag, target_env_, target_env_version_,195target_spirv_version_, target_spirv_version_is_forced_);196if (!target_client_info.error.empty()) {197*error_stream << target_client_info.error;198*total_warnings = 0;199*total_errors = 1;200return result_tuple;201}202203EShLanguage used_shader_stage = forced_shader_stage;204const std::string macro_definitions =205shaderc_util::format(predefined_macros_, "#define ", " ", "\n");206const std::string pound_extension =207"#extension GL_GOOGLE_include_directive : enable\n";208const std::string preamble = macro_definitions + pound_extension;209210std::string preprocessed_shader;211212// If only preprocessing, we definitely need to preprocess. Otherwise, if213// we don't know the stage until now, we need the preprocessed shader to214// deduce the shader stage.215if (output_type == OutputType::PreprocessedText ||216used_shader_stage == EShLangCount) {217bool success;218std::string glslang_errors;219std::tie(success, preprocessed_shader, glslang_errors) =220PreprocessShader(error_tag, input_source_string, preamble, includer);221222success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,223/* suppress_warnings = */ true,224glslang_errors.c_str(), total_warnings,225total_errors);226if (!success) return result_tuple;227// Because of the behavior change of the #line directive, the #line228// directive introducing each file's content must use the syntax for the229// specified version. So we need to probe this shader's version and230// profile.231int version;232EProfile profile;233std::tie(version, profile) = DeduceVersionProfile(preprocessed_shader);234const bool is_for_next_line = LineDirectiveIsForNextLine(version, profile);235236preprocessed_shader =237CleanupPreamble(preprocessed_shader, error_tag, pound_extension,238includer.num_include_directives(), is_for_next_line);239240if (output_type == OutputType::PreprocessedText) {241// Set the values of the result tuple.242succeeded = true;243compilation_output_data = ConvertStringToVector(preprocessed_shader);244compilation_output_data_size_in_bytes = preprocessed_shader.size();245return result_tuple;246} else if (used_shader_stage == EShLangCount) {247std::string errors;248std::tie(used_shader_stage, errors) =249GetShaderStageFromSourceCode(error_tag, preprocessed_shader);250if (!errors.empty()) {251*error_stream << errors;252return result_tuple;253}254if (used_shader_stage == EShLangCount) {255if ((used_shader_stage = stage_callback(error_stream, error_tag)) ==256EShLangCount) {257return result_tuple;258}259}260}261}262263// Parsing requires its own Glslang symbol tables.264glslang::TShader shader(used_shader_stage);265const char* shader_strings = input_source_string.data();266const int shader_lengths = static_cast<int>(input_source_string.size());267const char* string_names = error_tag.c_str();268shader.setStringsWithLengthsAndNames(&shader_strings, &shader_lengths,269&string_names, 1);270shader.setPreamble(preamble.c_str());271shader.setEntryPoint(entry_point_name);272shader.setAutoMapBindings(auto_bind_uniforms_);273if (auto_combined_image_sampler_) {274shader.setTextureSamplerTransformMode(EShTexSampTransUpgradeTextureRemoveSampler);275}276shader.setAutoMapLocations(auto_map_locations_);277const auto& bases = auto_binding_base_[static_cast<int>(used_shader_stage)];278shader.setShiftImageBinding(bases[static_cast<int>(UniformKind::Image)]);279shader.setShiftSamplerBinding(bases[static_cast<int>(UniformKind::Sampler)]);280shader.setShiftTextureBinding(bases[static_cast<int>(UniformKind::Texture)]);281shader.setShiftUboBinding(bases[static_cast<int>(UniformKind::Buffer)]);282shader.setShiftSsboBinding(283bases[static_cast<int>(UniformKind::StorageBuffer)]);284shader.setShiftUavBinding(285bases[static_cast<int>(UniformKind::UnorderedAccessView)]);286shader.setHlslIoMapping(hlsl_iomap_);287shader.setResourceSetBinding(288hlsl_explicit_bindings_[static_cast<int>(used_shader_stage)]);289shader.setEnvClient(target_client_info.client,290target_client_info.client_version);291shader.setEnvTarget(target_client_info.target_language,292target_client_info.target_language_version);293if (hlsl_functionality1_enabled_) {294shader.setEnvTargetHlslFunctionality1();295}296if (vulkan_rules_relaxed_) {297glslang::EShSource language = glslang::EShSourceNone;298switch(source_language_) {299case SourceLanguage::GLSL:300language = glslang::EShSourceGlsl;301break;302case SourceLanguage::HLSL:303language = glslang::EShSourceHlsl;304break;305}306// This option will only be used if the Vulkan client is used.307// If new versions of GL_KHR_vulkan_glsl come out, it would make sense to308// let callers specify which version to use. For now, just use 100.309shader.setEnvInput(language, used_shader_stage, glslang::EShClientVulkan, 100);310shader.setEnvInputVulkanRulesRelaxed();311}312shader.setInvertY(invert_y_enabled_);313shader.setNanMinMaxClamp(nan_clamp_);314315const EShMessages rules =316GetMessageRules(target_env_, source_language_, hlsl_offsets_,317hlsl_16bit_types_enabled_, generate_debug_info_);318319bool success = shader.parse(&limits_, default_version_, default_profile_,320force_version_profile_, kNotForwardCompatible,321rules, includer);322323success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,324suppress_warnings_, shader.getInfoLog(),325total_warnings, total_errors);326if (!success) return result_tuple;327328glslang::TProgram program;329program.addShader(&shader);330success = program.link(EShMsgDefault) && program.mapIO();331success &= PrintFilteredErrors(error_tag, error_stream, warnings_as_errors_,332suppress_warnings_, program.getInfoLog(),333total_warnings, total_errors);334if (!success) return result_tuple;335336// 'spirv' is an alias for the compilation_output_data. This alias is added337// to serve as an input for the call to DissassemblyBinary.338std::vector<uint32_t>& spirv = compilation_output_data;339glslang::SpvOptions options;340options.generateDebugInfo = generate_debug_info_;341options.disableOptimizer = true;342options.optimizeSize = false;343// Note the call to GlslangToSpv also populates compilation_output_data.344glslang::GlslangToSpv(*program.getIntermediate(used_shader_stage), spirv,345&options);346347// Set the tool field (the top 16-bits) in the generator word to348// 'Shaderc over Glslang'.349const uint32_t shaderc_generator_word = 13; // From SPIR-V XML Registry350const uint32_t generator_word_index = 2; // SPIR-V 2.3: Physical layout351assert(spirv.size() > generator_word_index);352spirv[generator_word_index] =353(spirv[generator_word_index] & 0xffff) | (shaderc_generator_word << 16);354355std::vector<PassId> opt_passes;356357if (hlsl_legalization_enabled_ && source_language_ == SourceLanguage::HLSL) {358// If from HLSL, run this passes to "legalize" the SPIR-V for Vulkan359// eg. forward and remove memory writes of opaque types.360opt_passes.push_back(PassId::kLegalizationPasses);361}362363opt_passes.insert(opt_passes.end(), enabled_opt_passes_.begin(),364enabled_opt_passes_.end());365366if (!opt_passes.empty()) {367spvtools::OptimizerOptions opt_options;368opt_options.set_preserve_bindings(preserve_bindings_);369370std::string opt_errors;371if (!SpirvToolsOptimize(target_env_, target_env_version_, opt_passes,372opt_options, &spirv, &opt_errors)) {373*error_stream << "shaderc: internal error: compilation succeeded but "374"failed to optimize: "375<< opt_errors << "\n";376return result_tuple;377}378}379380if (output_type == OutputType::SpirvAssemblyText) {381std::string text_or_error;382if (!SpirvToolsDisassemble(target_env_, target_env_version_, spirv,383&text_or_error)) {384*error_stream << "shaderc: internal error: compilation succeeded but "385"failed to disassemble: "386<< text_or_error << "\n";387return result_tuple;388}389succeeded = true;390compilation_output_data = ConvertStringToVector(text_or_error);391compilation_output_data_size_in_bytes = text_or_error.size();392return result_tuple;393} else {394succeeded = true;395// Note compilation_output_data is already populated in GlslangToSpv().396compilation_output_data_size_in_bytes = spirv.size() * sizeof(spirv[0]);397return result_tuple;398}399}400401void Compiler::AddMacroDefinition(const char* macro, size_t macro_length,402const char* definition,403size_t definition_length) {404predefined_macros_[std::string(macro, macro_length)] =405definition ? std::string(definition, definition_length) : "";406}407408void Compiler::SetTargetEnv(Compiler::TargetEnv env,409Compiler::TargetEnvVersion version) {410target_env_ = env;411target_env_version_ = version;412}413414void Compiler::SetTargetSpirv(Compiler::SpirvVersion version) {415target_spirv_version_ = version;416target_spirv_version_is_forced_ = true;417}418419void Compiler::SetSourceLanguage(Compiler::SourceLanguage lang) {420source_language_ = lang;421}422423void Compiler::SetForcedVersionProfile(int version, EProfile profile) {424default_version_ = version;425default_profile_ = profile;426force_version_profile_ = true;427}428429void Compiler::SetWarningsAsErrors() { warnings_as_errors_ = true; }430431void Compiler::SetGenerateDebugInfo() {432generate_debug_info_ = true;433for (size_t i = 0; i < enabled_opt_passes_.size(); ++i) {434if (enabled_opt_passes_[i] == PassId::kStripDebugInfo) {435enabled_opt_passes_[i] = PassId::kNullPass;436}437}438}439440void Compiler::SetOptimizationLevel(Compiler::OptimizationLevel level) {441// Clear previous settings first.442enabled_opt_passes_.clear();443444switch (level) {445case OptimizationLevel::Size:446if (!generate_debug_info_) {447enabled_opt_passes_.push_back(PassId::kStripDebugInfo);448}449enabled_opt_passes_.push_back(PassId::kSizePasses);450break;451case OptimizationLevel::Performance:452if (!generate_debug_info_) {453enabled_opt_passes_.push_back(PassId::kStripDebugInfo);454}455enabled_opt_passes_.push_back(PassId::kPerformancePasses);456break;457default:458break;459}460}461462void Compiler::EnableHlslLegalization(bool hlsl_legalization_enabled) {463hlsl_legalization_enabled_ = hlsl_legalization_enabled;464}465466void Compiler::EnableHlslFunctionality1(bool enable) {467hlsl_functionality1_enabled_ = enable;468}469470void Compiler::SetVulkanRulesRelaxed(bool enable) {471vulkan_rules_relaxed_ = enable;472}473474void Compiler::EnableHlsl16BitTypes(bool enable) {475hlsl_16bit_types_enabled_ = enable;476}477478void Compiler::EnableInvertY(bool enable) { invert_y_enabled_ = enable; }479480void Compiler::SetNanClamp(bool enable) { nan_clamp_ = enable; }481482void Compiler::SetSuppressWarnings() { suppress_warnings_ = true; }483484std::tuple<bool, std::string, std::string> Compiler::PreprocessShader(485const std::string& error_tag, const string_piece& shader_source,486const string_piece& shader_preamble, CountingIncluder& includer) const {487// The stage does not matter for preprocessing.488glslang::TShader shader(EShLangVertex);489const char* shader_strings = shader_source.data();490const int shader_lengths = static_cast<int>(shader_source.size());491const char* string_names = error_tag.c_str();492shader.setStringsWithLengthsAndNames(&shader_strings, &shader_lengths,493&string_names, 1);494shader.setPreamble(shader_preamble.data());495auto target_client_info = GetGlslangClientInfo(496error_tag, target_env_, target_env_version_,497target_spirv_version_, target_spirv_version_is_forced_);498if (!target_client_info.error.empty()) {499return std::make_tuple(false, "", target_client_info.error);500}501shader.setEnvClient(target_client_info.client,502target_client_info.client_version);503if (hlsl_functionality1_enabled_) {504shader.setEnvTargetHlslFunctionality1();505}506shader.setInvertY(invert_y_enabled_);507shader.setNanMinMaxClamp(nan_clamp_);508509// The preprocessor might be sensitive to the target environment.510// So combine the existing rules with the just-give-me-preprocessor-output511// flag.512const auto rules = static_cast<EShMessages>(513EShMsgOnlyPreprocessor |514GetMessageRules(target_env_, source_language_, hlsl_offsets_,515hlsl_16bit_types_enabled_, false));516517std::string preprocessed_shader;518const bool success = shader.preprocess(519&limits_, default_version_, default_profile_, force_version_profile_,520kNotForwardCompatible, rules, &preprocessed_shader, includer);521522if (success) {523return std::make_tuple(true, preprocessed_shader, shader.getInfoLog());524}525return std::make_tuple(false, "", shader.getInfoLog());526}527528std::string Compiler::CleanupPreamble(const string_piece& preprocessed_shader,529const string_piece& error_tag,530const string_piece& pound_extension,531int num_include_directives,532bool is_for_next_line) const {533// Those #define directives in preamble will become empty lines after534// preprocessing. We also injected an #extension directive to turn on #include535// directive support. In the original preprocessing output from glslang, it536// appears before the user source string. We need to do proper adjustment:537// * Remove empty lines generated from #define directives in preamble.538// * If there is no #include directive in the source code, we do not need to539// output the injected #extension directive. Otherwise,540// * If there exists a #version directive in the source code, it should be541// placed at the first line. Its original line will be filled with an empty542// line as placeholder to maintain the code structure.543544const std::vector<string_piece> lines =545preprocessed_shader.get_fields('\n', /* keep_delimiter = */ true);546547std::ostringstream output_stream;548549size_t pound_extension_index = lines.size();550size_t pound_version_index = lines.size();551for (size_t i = 0; i < lines.size(); ++i) {552if (lines[i] == pound_extension) {553pound_extension_index = std::min(i, pound_extension_index);554} else if (lines[i].starts_with("#version")) {555// In a preprocessed shader, directives are in a canonical format, so we556// can confidently compare to '#version' verbatim, without worrying about557// whitespace.558pound_version_index = i;559if (num_include_directives > 0) output_stream << lines[i];560break;561}562}563// We know that #extension directive exists and appears before #version564// directive (if any).565assert(pound_extension_index < lines.size());566567for (size_t i = 0; i < pound_extension_index; ++i) {568// All empty lines before the #line directive we injected are generated by569// preprocessing preamble. Do not output them.570if (lines[i].strip_whitespace().empty()) continue;571output_stream << lines[i];572}573574if (num_include_directives > 0) {575output_stream << pound_extension;576// Also output a #line directive for the main file.577output_stream << GetLineDirective(is_for_next_line, error_tag);578}579580for (size_t i = pound_extension_index + 1; i < lines.size(); ++i) {581if (i == pound_version_index) {582if (num_include_directives > 0) {583output_stream << "\n";584} else {585output_stream << lines[i];586}587} else {588output_stream << lines[i];589}590}591592return output_stream.str();593}594595std::pair<EShLanguage, std::string> Compiler::GetShaderStageFromSourceCode(596string_piece filename, const std::string& preprocessed_shader) const {597const string_piece kPragmaShaderStageDirective = "#pragma shader_stage";598const string_piece kLineDirective = "#line";599600int version;601EProfile profile;602std::tie(version, profile) = DeduceVersionProfile(preprocessed_shader);603const bool is_for_next_line = LineDirectiveIsForNextLine(version, profile);604605std::vector<string_piece> lines =606string_piece(preprocessed_shader).get_fields('\n');607// The filename, logical line number (which starts from 1 and is sensitive to608// #line directives), and stage value for #pragma shader_stage() directives.609std::vector<std::tuple<string_piece, size_t, string_piece>> stages;610// The physical line numbers of the first #pragma shader_stage() line and611// first non-preprocessing line in the preprocessed shader text.612size_t first_pragma_physical_line = lines.size() + 1;613size_t first_non_pp_line = lines.size() + 1;614615for (size_t i = 0, logical_line_no = 1; i < lines.size(); ++i) {616const string_piece current_line = lines[i].strip_whitespace();617if (current_line.starts_with(kPragmaShaderStageDirective)) {618const string_piece stage_value =619current_line.substr(kPragmaShaderStageDirective.size()).strip("()");620stages.emplace_back(filename, logical_line_no, stage_value);621first_pragma_physical_line = std::min(first_pragma_physical_line, i + 1);622} else if (!current_line.empty() && !current_line.starts_with("#")) {623first_non_pp_line = std::min(first_non_pp_line, i + 1);624}625626// Update logical line number for the next line.627if (current_line.starts_with(kLineDirective)) {628string_piece name;629std::tie(logical_line_no, name) = DecodeLineDirective(current_line);630if (!name.empty()) filename = name;631// Note that for core profile, the meaning of #line changed since version632// 330. The line number given by #line used to mean the logical line633// number of the #line line. Now it means the logical line number of the634// next line after the #line line.635if (!is_for_next_line) ++logical_line_no;636} else {637++logical_line_no;638}639}640if (stages.empty()) return std::make_pair(EShLangCount, "");641642std::string error_message;643644const string_piece& first_pragma_filename = std::get<0>(stages[0]);645const std::string first_pragma_line = std::to_string(std::get<1>(stages[0]));646const string_piece& first_pragma_stage = std::get<2>(stages[0]);647648if (first_pragma_physical_line > first_non_pp_line) {649error_message += first_pragma_filename.str() + ":" + first_pragma_line +650": error: '#pragma': the first 'shader_stage' #pragma "651"must appear before any non-preprocessing code\n";652}653654EShLanguage stage = MapStageNameToLanguage(first_pragma_stage);655if (stage == EShLangCount) {656error_message +=657first_pragma_filename.str() + ":" + first_pragma_line +658": error: '#pragma': invalid stage for 'shader_stage' #pragma: '" +659first_pragma_stage.str() + "'\n";660}661662for (size_t i = 1; i < stages.size(); ++i) {663const string_piece& current_stage = std::get<2>(stages[i]);664if (current_stage != first_pragma_stage) {665const string_piece& current_filename = std::get<0>(stages[i]);666const std::string current_line = std::to_string(std::get<1>(stages[i]));667error_message += current_filename.str() + ":" + current_line +668": error: '#pragma': conflicting stages for "669"'shader_stage' #pragma: '" +670current_stage.str() + "' (was '" +671first_pragma_stage.str() + "' at " +672first_pragma_filename.str() + ":" + first_pragma_line +673")\n";674}675}676677return std::make_pair(error_message.empty() ? stage : EShLangCount,678error_message);679}680681std::pair<int, EProfile> Compiler::DeduceVersionProfile(682const std::string& preprocessed_shader) const {683int version = default_version_;684EProfile profile = default_profile_;685if (!force_version_profile_) {686std::tie(version, profile) =687GetVersionProfileFromSourceCode(preprocessed_shader);688if (version == 0 && profile == ENoProfile) {689version = default_version_;690profile = default_profile_;691}692}693return std::make_pair(version, profile);694}695696std::pair<int, EProfile> Compiler::GetVersionProfileFromSourceCode(697const std::string& preprocessed_shader) const {698string_piece pound_version = preprocessed_shader;699const size_t pound_version_loc = pound_version.find("#version");700if (pound_version_loc == string_piece::npos) {701return std::make_pair(0, ENoProfile);702}703pound_version =704pound_version.substr(pound_version_loc + std::strlen("#version"));705pound_version = pound_version.substr(0, pound_version.find_first_of("\n"));706707std::string version_profile;708for (const auto character : pound_version) {709if (character != ' ') version_profile += character;710}711712int version;713EProfile profile;714if (!ParseVersionProfile(version_profile, &version, &profile)) {715return std::make_pair(0, ENoProfile);716}717return std::make_pair(version, profile);718}719720// Converts a string to a vector of uint32_t by copying the content of a given721// string to a vector<uint32_t> and returns it. Appends '\0' at the end if extra722// bytes are required to complete the last element.723std::vector<uint32_t> ConvertStringToVector(const std::string& str) {724size_t num_bytes_str = str.size() + 1u;725size_t vector_length =726(num_bytes_str + sizeof(uint32_t) - 1) / sizeof(uint32_t);727std::vector<uint32_t> result_vec(vector_length, 0);728std::strncpy(reinterpret_cast<char*>(result_vec.data()), str.c_str(),729str.size());730return result_vec;731}732733GlslangClientInfo GetGlslangClientInfo(734const std::string& error_tag, shaderc_util::Compiler::TargetEnv env,735shaderc_util::Compiler::TargetEnvVersion env_version,736shaderc_util::Compiler::SpirvVersion spv_version,737bool spv_version_is_forced) {738GlslangClientInfo result;739std::ostringstream errs;740741using shaderc_util::Compiler;742switch (env) {743case Compiler::TargetEnv::Vulkan:744result.client = glslang::EShClientVulkan;745if (env_version == Compiler::TargetEnvVersion::Default ||746env_version == Compiler::TargetEnvVersion::Vulkan_1_0) {747result.client_version = glslang::EShTargetVulkan_1_0;748} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_1) {749result.client_version = glslang::EShTargetVulkan_1_1;750result.target_language_version = glslang::EShTargetSpv_1_3;751} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_2) {752result.client_version = glslang::EShTargetVulkan_1_2;753result.target_language_version = glslang::EShTargetSpv_1_5;754} else if (env_version == Compiler::TargetEnvVersion::Vulkan_1_3) {755result.client_version = glslang::EShTargetVulkan_1_3;756result.target_language_version = glslang::EShTargetSpv_1_6;757} else {758errs << "error:" << error_tag << ": Invalid target client version "759<< static_cast<uint32_t>(env_version) << " for Vulkan environment "760<< int(env);761}762break;763case Compiler::TargetEnv::OpenGLCompat:764errs << "error: OpenGL compatibility profile is not supported";765break;766case Compiler::TargetEnv::OpenGL:767result.client = glslang::EShClientOpenGL;768if (env_version == Compiler::TargetEnvVersion::Default ||769env_version == Compiler::TargetEnvVersion::OpenGL_4_5) {770result.client_version = glslang::EShTargetOpenGL_450;771} else {772errs << "error:" << error_tag << ": Invalid target client version "773<< static_cast<uint32_t>(env_version) << " for OpenGL environment "774<< int(env);775}776break;777default:778errs << "error:" << error_tag << ": Invalid target client environment "779<< int(env);780break;781}782783if (spv_version_is_forced && errs.str().empty()) {784switch (spv_version) {785case Compiler::SpirvVersion::v1_0:786result.target_language_version = glslang::EShTargetSpv_1_0;787break;788case Compiler::SpirvVersion::v1_1:789result.target_language_version = glslang::EShTargetSpv_1_1;790break;791case Compiler::SpirvVersion::v1_2:792result.target_language_version = glslang::EShTargetSpv_1_2;793break;794case Compiler::SpirvVersion::v1_3:795result.target_language_version = glslang::EShTargetSpv_1_3;796break;797case Compiler::SpirvVersion::v1_4:798result.target_language_version = glslang::EShTargetSpv_1_4;799break;800case Compiler::SpirvVersion::v1_5:801result.target_language_version = glslang::EShTargetSpv_1_5;802break;803case Compiler::SpirvVersion::v1_6:804result.target_language_version = glslang::EShTargetSpv_1_6;805break;806default:807errs << "error:" << error_tag << ": Unknown SPIR-V version " << std::hex808<< uint32_t(spv_version);809break;810}811}812result.error = errs.str();813return result;814}815816} // namespace shaderc_util817818819