@@ -877,12 +877,36 @@ void CompilerMSL::build_implicit_builtins()
877877 if (need_position)
878878 {
879879 // If we can get away with returning void from entry point, we don't need to care.
880- // If there is at least one other stage output, we need to return [[position]].
881- need_position = false;
880+ // If there is at least one other stage output, we need to return [[position]],
881+ // so we need to create one if it doesn't appear in the SPIR-V. Before adding the
882+ // implicit variable, check if it actually exists already, but just has not been used
883+ // or initialized, and if so, mark it as active, and do not create the implicit variable.
884+ bool has_output = false;
882885 ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
883886 if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
884- need_position = true;
887+ {
888+ has_output = true;
889+
890+ // Check if the var is the Position builtin
891+ if (has_decoration(var.self, DecorationBuiltIn) && get_decoration(var.self, DecorationBuiltIn) == BuiltInPosition)
892+ active_output_builtins.set(BuiltInPosition);
893+
894+ // If the var is a struct, check if any members is the Position builtin
895+ auto &var_type = get_variable_element_type(var);
896+ if (var_type.basetype == SPIRType::Struct)
897+ {
898+ auto mbr_cnt = var_type.member_types.size();
899+ for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++)
900+ {
901+ auto builtin = BuiltInMax;
902+ bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin);
903+ if (is_builtin && builtin == BuiltInPosition)
904+ active_output_builtins.set(BuiltInPosition);
905+ }
906+ }
907+ }
885908 });
909+ need_position = has_output && !active_output_builtins.get(BuiltInPosition);
886910 }
887911
888912 if (need_position)
0 commit comments