diff --git a/reference/shaders-msl/vert/basic.vert b/reference/shaders-msl/vert/basic.vert index d2c96d2c..d84e6bc4 100644 --- a/reference/shaders-msl/vert/basic.vert +++ b/reference/shaders-msl/vert/basic.vert @@ -26,7 +26,6 @@ vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]] main0_out out = {}; out.gl_Position = _16.uMVP * in.aVertex; out.vNormal = in.aNormal; - out.gl_Position.y = -(out.gl_Position.y); // Invert Y-axis for Metal return out; } diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 6302939d..c3e630bf 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -2397,8 +2397,10 @@ SPIREntryPoint &Compiler::get_entry_point() bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const { auto &var = get(id); - if (var.storage != StorageClassInput && var.storage != StorageClassOutput) - SPIRV_CROSS_THROW("Only Input and Output variables are part of a shader linking interface."); + if (var.storage != StorageClassInput && var.storage != StorageClassOutput && + var.storage != StorageClassUniformConstant) + throw CompilerError( + "Only Input, Output variables and Uniform constants are part of a shader linking interface."); // This is to avoid potential problems with very old glslang versions which did // not emit input/output interfaces properly. diff --git a/spirv_msl.cpp b/spirv_msl.cpp index b82c7894..7b18b455 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -29,6 +29,7 @@ CompilerMSL::CompilerMSL(vector spirv_) options.vertex.fixup_clipspace = false; populate_func_name_overrides(); + populate_var_name_overrides(); } // Populate the collection of function names that need to be overridden @@ -38,6 +39,12 @@ void CompilerMSL::populate_func_name_overrides() func_name_overrides["saturate"] = "saturate0"; } +void CompilerMSL::populate_var_name_overrides() +{ + var_name_overrides["kernel"] = "kernel0"; + var_name_overrides["bias"] = "bias0"; +} + string CompilerMSL::compile(MSLConfiguration &msl_cfg, vector *p_vtx_attrs, std::vector *p_res_bindings) { @@ -47,6 +54,10 @@ string CompilerMSL::compile(MSLConfiguration &msl_cfg, vector *p_ // Remember the input parameters msl_config = msl_cfg; + // Set main function name if it was explicitly set + if (!msl_config.entry_point_name.empty()) + set_name(entry_point, msl_config.entry_point_name); + vtx_attrs_by_location.clear(); if (p_vtx_attrs) for (auto &va : *p_vtx_attrs) @@ -61,10 +72,11 @@ string CompilerMSL::compile(MSLConfiguration &msl_cfg, vector *p_ set_enabled_interface_variables(get_active_interface_variables()); preprocess_op_codes(); - // Create structs to hold input and output variables + // Create structs to hold input, output and uniform variables qual_pos_var_name = ""; stage_in_var_id = add_interface_block(StorageClassInput); stage_out_var_id = add_interface_block(StorageClassOutput); + stage_uniforms_var_id = add_interface_block(StorageClassUniformConstant); // Convert the use of global variables to recursively-passed function parameters localize_global_variables(); @@ -158,10 +170,17 @@ void CompilerMSL::extract_global_variables_from_functions() auto &var = id.get(); if (var.storage == StorageClassInput || var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || var.storage == StorageClassPushConstant) + { global_var_ids.insert(var.self); + } } } + // Local vars that are declared in the main function and accessed directy by a function + auto &entry_func = get(entry_point); + for (auto &var : entry_func.local_variables) + global_var_ids.insert(var); + std::unordered_set added_arg_ids; std::unordered_set processed_func_ids; extract_global_variables_from_function(entry_point, added_arg_ids, global_var_ids, processed_func_ids); @@ -176,7 +195,11 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: { // Avoid processing a function more than once if (processed_func_ids.find(func_id) != processed_func_ids.end()) + { + // Return function global variables + added_arg_ids = function_global_vars[func_id]; return; + } processed_func_ids.insert(func_id); @@ -199,6 +222,7 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: uint32_t base_id = ops[2]; if (global_var_ids.find(base_id) != global_var_ids.end()) added_arg_ids.insert(base_id); + break; } case OpFunctionCall: @@ -217,6 +241,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std:: } } + function_global_vars[func_id] = added_arg_ids; + // Add the global variables as arguments to the function if (func_id != entry_point) { @@ -312,6 +338,12 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) break; } + case StorageClassUniformConstant: + { + ib_var_ref = stage_uniform_var_name; + break; + } + default: break; } @@ -358,7 +390,11 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) mbr_idx++; } } - else + else if (type.basetype == SPIRType::Boolean || type.basetype == SPIRType::Char || + type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt || + type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64 || + type.basetype == SPIRType::Float || type.basetype == SPIRType::Double || + type.basetype == SPIRType::Boolean) { // Add a reference to the variable type to the interface struct. uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); @@ -375,7 +411,10 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) // Copy the variable location from the original variable to the member auto &dec = meta[p_var->self].decoration; uint32_t locn = dec.location; - set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn); + if (get_decoration_mask(p_var->self) & (1ull << DecorationLocation)) + { + set_member_decoration(ib_type_id, ib_mbr_idx, DecorationLocation, locn); + } mark_location_as_used_by_shader(locn, storage); // Mark the member as builtin if needed @@ -396,6 +435,15 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage) MemberSorter memberSorter(ib_type, meta[ib_type_id], sort_aspect); memberSorter.sort(); + // Sort input or output variables alphabetical + auto &execution = get_entry_point(); + if ((execution.model == ExecutionModelFragment && storage == StorageClassInput) || + (execution.model == ExecutionModelVertex && storage == StorageClassOutput)) + { + MemberSorter memberSorter(ib_type, meta[ib_type.self], MemberSorter::Alphabetical); + memberSorter.sort(); + } + return ib_var_id; } @@ -481,6 +529,7 @@ void CompilerMSL::emit_resources() // Output interface blocks. emit_interface_block(stage_in_var_id); emit_interface_block(stage_out_var_id); + emit_interface_block(stage_uniforms_var_id); } // Override for MSL-specific syntax instructions @@ -627,7 +676,9 @@ void CompilerMSL::emit_interface_block(uint32_t ib_var_id) { auto &ib_var = get(ib_var_id); auto &ib_type = get(ib_var.basetype); - emit_struct(ib_type); + auto &m = meta.at(ib_type.self); + if (m.members.size() > 0) + emit_struct(ib_type); } } @@ -1069,9 +1120,8 @@ void CompilerMSL::emit_fixup() { if (options.vertex.fixup_clipspace) { - const char *suffix = backend.float_literal_suffix ? "f" : ""; - statement(qual_pos_var_name, ".z = 2.0", suffix, " * ", qual_pos_var_name, ".z - ", qual_pos_var_name, - ".w;", " // Adjust clip-space for Metal"); + statement(qual_pos_var_name, ".z = (", qual_pos_var_name, ".z + ", qual_pos_var_name, + ".w) * 0.5; // Adjust clip-space for Metal"); } if (msl_config.flip_vert_y) @@ -1310,12 +1360,15 @@ string CompilerMSL::entry_point_args(bool append_comma) auto &type = get(var.basetype); if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || - var.storage == StorageClassPushConstant) && - !is_hidden_variable(var)) + var.storage == StorageClassPushConstant)) { switch (type.basetype) { case SPIRType::Struct: + { + auto &m = meta.at(type.self); + if (m.members.size() == 0) + break; if (!ep_args.empty()) ep_args += ", "; if ((meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0 && @@ -1330,6 +1383,7 @@ string CompilerMSL::entry_point_args(bool append_comma) ep_args += type_to_glsl(type) + "& " + to_name(var.self); ep_args += " [[buffer(" + convert_to_string(get_metal_resource_index(var, type.basetype)) + ")]]"; break; + } case SPIRType::Sampler: if (!ep_args.empty()) ep_args += ", "; @@ -1431,8 +1485,12 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) auto &type = expression_type(arg.id); bool constref = !type.pointer || arg.write_count == 0; + // TODO: Check if this arg is an uniform pointer + bool pointer = type.storage == StorageClassUniformConstant; + auto &var = get(arg.id); - return join(constref ? "const " : "", type_to_glsl(type), "& ", to_name(var.self), type_to_array_glsl(type)); + return join(constref ? "const " : "", type_to_glsl(type), pointer ? " " : "& ", to_name(var.self), + type_to_array_glsl(type)); } // If we're currently in the entry point function, and the object @@ -1468,9 +1526,14 @@ string CompilerMSL::to_qualified_member_name(const SPIRType &type, uint32_t inde string CompilerMSL::ensure_valid_name(string name, string pfx) { if (name.size() >= 2 && name[0] == '_' && isdigit(name[1])) + { return join(pfx, name); + } else - return name; + { + auto iter = var_name_overrides.find(name); + return (iter != var_name_overrides.end()) ? iter->second : name; + } } // Returns an MSL string describing the SPIR-V type @@ -1861,6 +1924,8 @@ bool CompilerMSL::MemberSorter::operator()(uint32_t mbr_idx1, uint32_t mbr_idx2) case OffsetThenLocationReverse: return (mbr_meta1.offset < mbr_meta2.offset) || ((mbr_meta1.offset == mbr_meta2.offset) && (mbr_meta1.location > mbr_meta2.location)); + case Alphabetical: + return mbr_meta1.alias > mbr_meta2.alias; default: return false; } diff --git a/spirv_msl.hpp b/spirv_msl.hpp index d6a902f7..d876283d 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -29,9 +29,10 @@ namespace spirv_cross // Options for compiling to Metal Shading Language struct MSLConfiguration { - bool flip_vert_y = true; - bool flip_frag_y = true; + bool flip_vert_y = false; + bool flip_frag_y = false; bool is_rendering_points = false; + std::string entry_point_name; }; // Defines MSL characteristics of a vertex attribute at a particular location. @@ -115,22 +116,26 @@ protected: uint32_t coord, uint32_t coord_components, uint32_t dref, uint32_t grad_x, uint32_t grad_y, uint32_t lod, uint32_t coffset, uint32_t offset, uint32_t bias, uint32_t comp, uint32_t sample, bool *p_forward) override; - std::string clean_func_name(std::string func_name) override; void preprocess_op_codes(); void emit_custom_functions(); void localize_global_variables(); void extract_global_variables_from_functions(); + + std::unordered_map> function_global_vars; void extract_global_variables_from_function(uint32_t func_id, std::unordered_set &added_arg_ids, std::unordered_set &global_var_ids, std::unordered_set &processed_func_ids); uint32_t add_interface_block(spv::StorageClass storage); void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage); + void emit_resources(); void emit_interface_block(uint32_t ib_var_id); void populate_func_name_overrides(); + void populate_var_name_overrides(); std::string func_type_decl(SPIRType &type); + std::string clean_func_name(std::string func_name) override; std::string entry_point_args(bool append_comma); std::string get_entry_point_name(); std::string to_qualified_member_name(const SPIRType &type, uint32_t index); @@ -148,15 +153,18 @@ protected: MSLConfiguration msl_config; std::unordered_map func_name_overrides; + std::unordered_map var_name_overrides; std::set custom_function_ops; std::unordered_map vtx_attrs_by_location; std::vector resource_bindings; MSLResourceBinding next_metal_resource_index; uint32_t stage_in_var_id = 0; uint32_t stage_out_var_id = 0; + uint32_t stage_uniforms_var_id = 0; std::string qual_pos_var_name; std::string stage_in_var_name = "in"; std::string stage_out_var_name = "out"; + std::string stage_uniform_var_name = "uniforms"; std::string sampler_name_suffix = "Smplr"; // OpcodeHandler that handles several MSL preprocessing operations. @@ -184,6 +192,7 @@ protected: LocationReverse, Offset, OffsetThenLocationReverse, + Alphabetical }; void sort();