diff --git a/main.cpp b/main.cpp index d08ccebf..0c47f744 100644 --- a/main.cpp +++ b/main.cpp @@ -286,9 +286,9 @@ static void print_resources(const Compiler &compiler, const ShaderResources &res uint64_t modes = compiler.get_execution_mode_mask(); fprintf(stderr, "Entry points:\n"); - auto entry_points = compiler.get_entry_points(); + auto entry_points = compiler.get_entry_points_and_stages(); for (auto &e : entry_points) - fprintf(stderr, " %s (%s)\n", e.c_str(), execution_model_to_str(compiler.get_entry_point(e).model)); + fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model)); fprintf(stderr, "\n"); fprintf(stderr, "Execution modes:\n"); @@ -467,8 +467,15 @@ struct CLIArguments vector interface_variable_renames; vector hlsl_attr_remap; string entry; + string entry_stage; - vector> entry_point_rename; + struct Rename + { + string old_name; + string new_name; + ExecutionModel execution_model; + }; + vector entry_point_rename; uint32_t iterations = 1; bool cpp = false; @@ -491,12 +498,12 @@ static void print_help() "[--hlsl] [--shader-model] [--hlsl-enable-compat] " "[--separate-shader-objects]" "[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name " - "components] [--extension ext] [--entry name] [--remove-unused-variables] " + "components] [--extension ext] [--entry name] [--stage ] [--remove-unused-variables] " "[--flatten-multidimensional-arrays] [--no-420pack-extension] " "[--remap-variable-type ] " "[--rename-interface-variable ] " "[--set-hlsl-vertex-input-semantic ] " - "[--rename-entry-point ] " + "[--rename-entry-point ] " "\n"); } @@ -611,6 +618,24 @@ void rename_interface_variable(Compiler &compiler, const vector &resou } } +static ExecutionModel stage_to_execution_model(const std::string &stage) +{ + if (stage == "vert") + return ExecutionModelVertex; + else if (stage == "frag") + return ExecutionModelFragment; + else if (stage == "comp") + return ExecutionModelGLCompute; + else if (stage == "tesc") + return ExecutionModelTessellationControl; + else if (stage == "tese") + return ExecutionModelTessellationEvaluation; + else if (stage == "geom") + return ExecutionModelGeometry; + else + SPIRV_CROSS_THROW("Invalid stage."); +} + static int main_inner(int argc, char *argv[]) { CLIArguments args; @@ -652,9 +677,11 @@ static int main_inner(int argc, char *argv[]) cbs.add("--rename-entry-point", [&args](CLIParser &parser) { auto old_name = parser.next_string(); auto new_name = parser.next_string(); - args.entry_point_rename.push_back({ old_name, new_name }); + auto model = stage_to_execution_model(parser.next_string()); + args.entry_point_rename.push_back({ old_name, new_name, move(model) }); }); cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); }); + cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); }); cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; }); cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) { HLSLVertexAttributeRemap remap; @@ -772,10 +799,85 @@ static int main_inner(int argc, char *argv[]) } for (auto &rename : args.entry_point_rename) - compiler->rename_entry_point(rename.first, rename.second); + compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model); - if (!args.entry.empty()) - compiler->set_entry_point(args.entry); + auto entry_points = compiler->get_entry_points_and_stages(); + auto entry_point = args.entry; + ExecutionModel model = ExecutionModelMax; + + if (!args.entry_stage.empty()) + { + model = stage_to_execution_model(args.entry_stage); + if (entry_point.empty()) + { + // Just use the first entry point with this stage. + for (auto &e : entry_points) + { + if (e.execution_model == model) + { + entry_point = e.name; + break; + } + } + + if (entry_point.empty()) + { + fprintf(stderr, "Could not find an entry point with stage: %s\n", + args.entry_stage.c_str()); + return EXIT_FAILURE; + } + } + else + { + // Make sure both stage and name exists. + bool exists = false; + for (auto &e : entry_points) + { + if (e.execution_model == model && e.name == entry_point) + { + exists = true; + break; + } + } + + if (!exists) + { + fprintf(stderr, "Could not find an entry point %s with stage: %s\n", + entry_point.c_str(), args.entry_stage.c_str()); + return EXIT_FAILURE; + } + } + } + else if (!entry_point.empty()) + { + // Make sure there is just one entry point with this name, or the stage + // is ambiguous. + uint32_t stage_count = 0; + for (auto &e : entry_points) + { + if (e.name == entry_point) + { + stage_count++; + model = e.execution_model; + } + } + + if (stage_count == 0) + { + fprintf(stderr, "There is no entry point with name: %s\n", + entry_point.c_str()); + return EXIT_FAILURE; + } + else if (stage_count > 1) + { + fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", + entry_point.c_str()); + return EXIT_FAILURE; + } + } + + if (!entry_point.empty()) + compiler->set_entry_point(entry_point, model); if (!args.set_version && !compiler->get_options().version) { diff --git a/shaders-other/README.md b/shaders-other/README.md new file mode 100644 index 00000000..6d454813 --- /dev/null +++ b/shaders-other/README.md @@ -0,0 +1,4 @@ +These shaders are not actually run yet as part of any test suite, +but are kept here because they have been used to manually test various aspects of SPIRV-Cross in the past. + +These would ideally be part of the test suite in some way. diff --git a/shaders-other/aliased-entry-point-names.asm b/shaders-other/aliased-entry-point-names.asm new file mode 100644 index 00000000..d60cf303 --- /dev/null +++ b/shaders-other/aliased-entry-point-names.asm @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpEntryPoint Vertex %main2 "main2" %_ + OpEntryPoint Fragment %main3 "main" %FragColor + OpEntryPoint Fragment %main4 "main2" %FragColor + OpSource GLSL 450 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %FragColor Location 0 + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v4floatptr = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %FragColor = OpVariable %v4floatptr Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_2 = OpConstant %float 2 + %18 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %6 = OpLabel + %20 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %20 %18 + OpReturn + OpFunctionEnd + %main3 = OpFunction %void None %3 + %7 = OpLabel + OpStore %FragColor %17 + OpReturn + OpFunctionEnd + %main4 = OpFunction %void None %3 + %8 = OpLabel + OpStore %FragColor %18 + OpReturn + OpFunctionEnd diff --git a/spirv_cross.cpp b/spirv_cross.cpp index a5808529..2799317d 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -2589,20 +2589,51 @@ vector Compiler::get_entry_points() const return entries; } +vector Compiler::get_entry_points_and_stages() const +{ + vector entries; + for (auto &entry : entry_points) + entries.push_back({ entry.second.orig_name, entry.second.model }); + return entries; +} + void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name) { - auto &entry = get_entry_point(old_name); + auto &entry = get_first_entry_point(old_name); + entry.orig_name = new_name; + entry.name = new_name; +} + +void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel model) +{ + auto &entry = get_entry_point(old_name, model); entry.orig_name = new_name; entry.name = new_name; } void Compiler::set_entry_point(const std::string &name) { - auto &entry = get_entry_point(name); + auto &entry = get_first_entry_point(name); + entry_point = entry.self; +} + +void Compiler::set_entry_point(const std::string &name, spv::ExecutionModel model) +{ + auto &entry = get_entry_point(name, model); entry_point = entry.self; } SPIREntryPoint &Compiler::get_entry_point(const std::string &name) +{ + return get_first_entry_point(name); +} + +const SPIREntryPoint &Compiler::get_entry_point(const std::string &name) const +{ + return get_first_entry_point(name); +} + +SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) { auto itr = find_if(begin(entry_points), end(entry_points), [&](const std::pair &entry) -> bool { @@ -2615,7 +2646,7 @@ SPIREntryPoint &Compiler::get_entry_point(const std::string &name) return itr->second; } -const SPIREntryPoint &Compiler::get_entry_point(const std::string &name) const +const SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) const { auto itr = find_if(begin(entry_points), end(entry_points), [&](const std::pair &entry) -> bool { @@ -2628,9 +2659,40 @@ const SPIREntryPoint &Compiler::get_entry_point(const std::string &name) const return itr->second; } +SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) +{ + auto itr = + find_if(begin(entry_points), end(entry_points), [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) const +{ + auto itr = + find_if(begin(entry_points), end(entry_points), [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + const string &Compiler::get_cleansed_entry_point_name(const std::string &name) const { - return get_entry_point(name).name; + return get_first_entry_point(name).name; +} + +const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const +{ + return get_entry_point(name, model).name; } const SPIREntryPoint &Compiler::get_entry_point() const diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 69079232..22e9d4e6 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -108,6 +108,12 @@ enum BufferPackingStandard BufferPackingHLSLCbufferPackOffset }; +struct EntryPoint +{ + std::string name; + spv::ExecutionModel execution_model; +}; + class Compiler { public: @@ -261,17 +267,22 @@ public: // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. // Resource reflection also depends on the entry point. // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. + SPIRV_CROSS_DEPRECATED("Please use get_entry_points_and_stages instead.") std::vector get_entry_points() const; + SPIRV_CROSS_DEPRECATED("Please use set_entry_point(const std::string &, spv::ExecutionModel) instead.") void set_entry_point(const std::string &name); // Renames an entry point from old_name to new_name. // If old_name is currently selected as the current entry point, it will continue to be the current entry point, // albeit with a new name. // get_entry_points() is essentially invalidated at this point. + SPIRV_CROSS_DEPRECATED("Please use rename_entry_point(const std::string&, const std::string&, spv::ExecutionModel) instead.") void rename_entry_point(const std::string &old_name, const std::string &new_name); // Returns the internal data structure for entry points to allow poking around. + SPIRV_CROSS_DEPRECATED("Please use get_entry_point(const std::string &, spv::ExecutionModel instead.") const SPIREntryPoint &get_entry_point(const std::string &name) const; + SPIRV_CROSS_DEPRECATED("Please use get_entry_point(const std::string &, spv::ExecutionModel instead.") SPIREntryPoint &get_entry_point(const std::string &name); // Some shader languages restrict the names that can be given to entry points, and the @@ -282,8 +293,19 @@ public: // the name, as updated by the backend during the call to compile(). If the name is not // illegal, and has not been renamed, or if this function is called before compile(), // this function will simply return the same name. + SPIRV_CROSS_DEPRECATED("Please use get_cleansed_entry_point_name(const std::string &, spv::ExecutionModel) instead.") const std::string &get_cleansed_entry_point_name(const std::string &name) const; + // New variants of entry point query and reflection. + // Names for entry points in the SPIR-V module may alias if they belong to different execution models. + // To disambiguate, we must pass along with the entry point names the execution model. + std::vector get_entry_points_and_stages() const; + void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model); + void rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel execution_model); + const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const; + SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model); + const std::string &get_cleansed_entry_point_name(const std::string &name, spv::ExecutionModel execution_model) const; + // Query and modify OpExecutionMode. uint64_t get_execution_mode_mask() const; void unset_execution_mode(spv::ExecutionMode mode); @@ -792,6 +814,11 @@ protected: bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, uint32_t length); + +private: + // Used only to implement the old deprecated get_entry_point() interface. + const SPIREntryPoint &get_first_entry_point(const std::string &name) const; + SPIREntryPoint &get_first_entry_point(const std::string &name); }; }