Бранимир Караџић пре 4 година
родитељ
комит
396717efb8

+ 12 - 0
3rdparty/spirv-cross/spirv_common.hpp

@@ -302,8 +302,20 @@ struct Instruction
 {
 {
 	uint16_t op = 0;
 	uint16_t op = 0;
 	uint16_t count = 0;
 	uint16_t count = 0;
+	// If offset is 0 (not a valid offset into the instruction stream),
+	// we have an instruction stream which is embedded in the object.
 	uint32_t offset = 0;
 	uint32_t offset = 0;
 	uint32_t length = 0;
 	uint32_t length = 0;
+
+	inline bool is_embedded() const
+	{
+		return offset == 0;
+	}
+};
+
+struct EmbeddedInstruction : Instruction
+{
+	SmallVector<uint32_t> ops;
 };
 };
 
 
 enum Types
 enum Types

+ 12 - 3
3rdparty/spirv-cross/spirv_cross.hpp

@@ -513,9 +513,18 @@ protected:
 		if (!instr.length)
 		if (!instr.length)
 			return nullptr;
 			return nullptr;
 
 
-		if (instr.offset + instr.length > ir.spirv.size())
-			SPIRV_CROSS_THROW("Compiler::stream() out of range.");
-		return &ir.spirv[instr.offset];
+		if (instr.is_embedded())
+		{
+			auto &embedded = static_cast<const EmbeddedInstruction &>(instr);
+			assert(embedded.ops.size() == instr.length);
+			return embedded.ops.data();
+		}
+		else
+		{
+			if (instr.offset + instr.length > ir.spirv.size())
+				SPIRV_CROSS_THROW("Compiler::stream() out of range.");
+			return &ir.spirv[instr.offset];
+		}
 	}
 	}
 
 
 	ParsedIR ir;
 	ParsedIR ir;

+ 142 - 20
3rdparty/spirv-cross/spirv_glsl.cpp

@@ -40,6 +40,13 @@ using namespace spv;
 using namespace SPIRV_CROSS_NAMESPACE;
 using namespace SPIRV_CROSS_NAMESPACE;
 using namespace std;
 using namespace std;
 
 
+enum ExtraSubExpressionType
+{
+	// Create masks above any legal ID range to allow multiple address spaces into the extra_sub_expressions map.
+	EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET = 0x10000000,
+	EXTRA_SUB_EXPRESSION_TYPE_AUX = 0x20000000
+};
+
 static bool is_unsigned_opcode(Op op)
 static bool is_unsigned_opcode(Op op)
 {
 {
 	// Don't have to be exhaustive, only relevant for legacy target checking ...
 	// Don't have to be exhaustive, only relevant for legacy target checking ...
@@ -3601,6 +3608,21 @@ void CompilerGLSL::emit_output_variable_initializer(const SPIRVariable &var)
 			statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];");
 			statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];");
 		});
 		});
 	}
 	}
+	else if (has_decoration(var.self, DecorationBuiltIn) &&
+	         BuiltIn(get_decoration(var.self, DecorationBuiltIn)) == BuiltInSampleMask)
+	{
+		// We cannot copy the array since gl_SampleMask is unsized in GLSL. Unroll time! <_<
+		entry_func.fixup_hooks_in.push_back([&] {
+			auto &c = this->get<SPIRConstant>(var.initializer);
+			uint32_t num_constants = uint32_t(c.subconstants.size());
+			for (uint32_t i = 0; i < num_constants; i++)
+			{
+				// Don't use to_expression on constant since it might be uint, just fish out the raw int.
+				statement(to_expression(var.self), "[", i, "] = ",
+				          convert_to_string(this->get<SPIRConstant>(c.subconstants[i]).scalar_i32()), ";");
+			}
+		});
+	}
 	else
 	else
 	{
 	{
 		auto lut_name = join("_", var.self, "_init");
 		auto lut_name = join("_", var.self, "_init");
@@ -5700,14 +5722,27 @@ void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result
 	// Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends.
 	// Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends.
 	expected_type.basetype = input_type;
 	expected_type.basetype = input_type;
 	expected_type.width = expr_type.width;
 	expected_type.width = expr_type.width;
-	string cast_op = expr_type.basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0);
+
+	string cast_op;
+	if (expr_type.basetype != input_type)
+	{
+		if (expr_type.basetype == SPIRType::Boolean)
+			cast_op = join(type_to_glsl(expected_type), "(", to_unpacked_expression(op0), ")");
+		else
+			cast_op = bitcast_glsl(expected_type, op0);
+	}
+	else
+		cast_op = to_unpacked_expression(op0);
 
 
 	string expr;
 	string expr;
 	if (out_type.basetype != expected_result_type)
 	if (out_type.basetype != expected_result_type)
 	{
 	{
 		expected_type.basetype = expected_result_type;
 		expected_type.basetype = expected_result_type;
 		expected_type.width = out_type.width;
 		expected_type.width = out_type.width;
-		expr = bitcast_glsl_op(out_type, expected_type);
+		if (out_type.basetype == SPIRType::Boolean)
+			expr = type_to_glsl(out_type);
+		else
+			expr = bitcast_glsl_op(out_type, expected_type);
 		expr += '(';
 		expr += '(';
 		expr += join(op, "(", cast_op, ")");
 		expr += join(op, "(", cast_op, ")");
 		expr += ')';
 		expr += ')';
@@ -7364,7 +7399,7 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 	{
 	{
 		// Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op.
 		// Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op.
 		// IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags.
 		// IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags.
-		uint32_t &max_id = extra_sub_expressions[id | 0x80000000u];
+		uint32_t &max_id = extra_sub_expressions[id | EXTRA_SUB_EXPRESSION_TYPE_AUX];
 		if (!max_id)
 		if (!max_id)
 			max_id = ir.increase_bound_by(1);
 			max_id = ir.increase_bound_by(1);
 
 
@@ -7646,6 +7681,9 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i)
 	case OpGroupNonUniformBitwiseAnd:
 	case OpGroupNonUniformBitwiseAnd:
 	case OpGroupNonUniformBitwiseOr:
 	case OpGroupNonUniformBitwiseOr:
 	case OpGroupNonUniformBitwiseXor:
 	case OpGroupNonUniformBitwiseXor:
+	case OpGroupNonUniformLogicalAnd:
+	case OpGroupNonUniformLogicalOr:
+	case OpGroupNonUniformLogicalXor:
 	{
 	{
 		auto operation = static_cast<GroupOperation>(ops[3]);
 		auto operation = static_cast<GroupOperation>(ops[3]);
 		if (operation == GroupOperationClusteredReduce)
 		if (operation == GroupOperationClusteredReduce)
@@ -7802,6 +7840,9 @@ case OpGroupNonUniform##op: \
 	GLSL_GROUP_OP(BitwiseAnd, And)
 	GLSL_GROUP_OP(BitwiseAnd, And)
 	GLSL_GROUP_OP(BitwiseOr, Or)
 	GLSL_GROUP_OP(BitwiseOr, Or)
 	GLSL_GROUP_OP(BitwiseXor, Xor)
 	GLSL_GROUP_OP(BitwiseXor, Xor)
+	GLSL_GROUP_OP(LogicalAnd, And)
+	GLSL_GROUP_OP(LogicalOr, Or)
+	GLSL_GROUP_OP(LogicalXor, Xor)
 #undef GLSL_GROUP_OP
 #undef GLSL_GROUP_OP
 #undef GLSL_GROUP_OP_CAST
 #undef GLSL_GROUP_OP_CAST
 		// clang-format on
 		// clang-format on
@@ -9638,17 +9679,20 @@ void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_ex
 	{
 	{
 		handle_store_to_invariant_variable(lhs_expression, rhs_expression);
 		handle_store_to_invariant_variable(lhs_expression, rhs_expression);
 
 
-		auto lhs = to_dereferenced_expression(lhs_expression);
+		if (!unroll_array_to_complex_store(lhs_expression, rhs_expression))
+		{
+			auto lhs = to_dereferenced_expression(lhs_expression);
 
 
-		// We might need to cast in order to store to a builtin.
-		cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
+			// We might need to cast in order to store to a builtin.
+			cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
 
 
-		// Tries to optimize assignments like "<lhs> = <lhs> op expr".
-		// While this is purely cosmetic, this is important for legacy ESSL where loop
-		// variable increments must be in either i++ or i += const-expr.
-		// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
-		if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
-			statement(lhs, " = ", rhs, ";");
+			// Tries to optimize assignments like "<lhs> = <lhs> op expr".
+			// While this is purely cosmetic, this is important for legacy ESSL where loop
+			// variable increments must be in either i++ or i += const-expr.
+			// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
+			if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
+				statement(lhs, " = ", rhs, ";");
+		}
 		register_write(lhs_expression);
 		register_write(lhs_expression);
 	}
 	}
 }
 }
@@ -9810,6 +9854,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		// We might be trying to load a gl_Position[N], where we should be
 		// We might be trying to load a gl_Position[N], where we should be
 		// doing float4[](gl_in[i].gl_Position, ...) instead.
 		// doing float4[](gl_in[i].gl_Position, ...) instead.
 		// Similar workarounds are required for input arrays in tessellation.
 		// Similar workarounds are required for input arrays in tessellation.
+		// Also, loading from gl_SampleMask array needs special unroll.
 		unroll_array_from_complex_load(id, ptr, expr);
 		unroll_array_from_complex_load(id, ptr, expr);
 
 
 		// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
 		// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
@@ -10314,10 +10359,27 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		uint32_t rhs = ops[1];
 		uint32_t rhs = ops[1];
 		if (lhs != rhs)
 		if (lhs != rhs)
 		{
 		{
-			flush_variable_declaration(lhs);
-			flush_variable_declaration(rhs);
-			statement(to_expression(lhs), " = ", to_unpacked_expression(rhs), ";");
-			register_write(lhs);
+			uint32_t &tmp_id = extra_sub_expressions[instruction.offset | EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET];
+			if (!tmp_id)
+				tmp_id = ir.increase_bound_by(1);
+			uint32_t tmp_type_id = expression_type(rhs).parent_type;
+
+			EmbeddedInstruction fake_load, fake_store;
+			fake_load.op = OpLoad;
+			fake_load.length = 3;
+			fake_load.ops.push_back(tmp_type_id);
+			fake_load.ops.push_back(tmp_id);
+			fake_load.ops.push_back(rhs);
+
+			fake_store.op = OpStore;
+			fake_store.length = 2;
+			fake_store.ops.push_back(lhs);
+			fake_store.ops.push_back(tmp_id);
+
+			// Load and Store do a *lot* of workarounds, and we'd like to reuse them as much as possible.
+			// Synthesize a fake Load and Store pair for CopyMemory.
+			emit_instruction(fake_load);
+			emit_instruction(fake_store);
 		}
 		}
 		break;
 		break;
 	}
 	}
@@ -12148,6 +12210,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 	case OpGroupNonUniformBitwiseAnd:
 	case OpGroupNonUniformBitwiseAnd:
 	case OpGroupNonUniformBitwiseOr:
 	case OpGroupNonUniformBitwiseOr:
 	case OpGroupNonUniformBitwiseXor:
 	case OpGroupNonUniformBitwiseXor:
+	case OpGroupNonUniformLogicalAnd:
+	case OpGroupNonUniformLogicalOr:
+	case OpGroupNonUniformLogicalXor:
 	case OpGroupNonUniformQuadSwap:
 	case OpGroupNonUniformQuadSwap:
 	case OpGroupNonUniformQuadBroadcast:
 	case OpGroupNonUniformQuadBroadcast:
 		emit_subgroup_op(instruction);
 		emit_subgroup_op(instruction);
@@ -12791,7 +12856,7 @@ string CompilerGLSL::variable_decl(const SPIRVariable &variable)
 	// Ignore the pointer type since GLSL doesn't have pointers.
 	// Ignore the pointer type since GLSL doesn't have pointers.
 	auto &type = get_variable_data_type(variable);
 	auto &type = get_variable_data_type(variable);
 
 
-	if (type.pointer_depth > 1)
+	if (type.pointer_depth > 1 && !backend.support_pointer_to_pointer)
 		SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types.");
 		SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types.");
 
 
 	auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self));
 	auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self));
@@ -14920,6 +14985,43 @@ void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCl
 	statement(lhs, " = ", to_expression(rhs_id), ";");
 	statement(lhs, " = ", to_expression(rhs_id), ";");
 }
 }
 
 
+bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id)
+{
+	if (!backend.force_gl_in_out_block)
+		return false;
+	// This path is only relevant for GL backends.
+
+	auto *var = maybe_get<SPIRVariable>(target_id);
+	if (!var || var->storage != StorageClassOutput)
+		return false;
+
+	if (!is_builtin_variable(*var) || BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSampleMask)
+		return false;
+
+	auto &type = expression_type(source_id);
+	string array_expr;
+	if (type.array_size_literal.back())
+	{
+		array_expr = convert_to_string(type.array.back());
+		if (type.array.back() == 0)
+			SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array.");
+	}
+	else
+		array_expr = to_expression(type.array.back());
+
+	SPIRType target_type;
+	target_type.basetype = SPIRType::Int;
+
+	statement("for (int i = 0; i < int(", array_expr, "); i++)");
+	begin_scope();
+	statement(to_expression(target_id), "[i] = ",
+	          bitcast_expression(target_type, type.basetype, join(to_expression(source_id), "[i]")),
+	          ";");
+	end_scope();
+
+	return true;
+}
+
 void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr)
 void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr)
 {
 {
 	if (!backend.force_gl_in_out_block)
 	if (!backend.force_gl_in_out_block)
@@ -14930,7 +15032,7 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
 	if (!var)
 	if (!var)
 		return;
 		return;
 
 
-	if (var->storage != StorageClassInput)
+	if (var->storage != StorageClassInput && var->storage != StorageClassOutput)
 		return;
 		return;
 
 
 	auto &type = get_variable_data_type(*var);
 	auto &type = get_variable_data_type(*var);
@@ -14938,9 +15040,13 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
 		return;
 		return;
 
 
 	auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn));
 	auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn));
-	bool is_builtin = is_builtin_variable(*var) && (builtin == BuiltInPointSize || builtin == BuiltInPosition);
+	bool is_builtin = is_builtin_variable(*var) &&
+	                  (builtin == BuiltInPointSize ||
+	                   builtin == BuiltInPosition ||
+	                   builtin == BuiltInSampleMask);
 	bool is_tess = is_tessellation_shader();
 	bool is_tess = is_tessellation_shader();
 	bool is_patch = has_decoration(var->self, DecorationPatch);
 	bool is_patch = has_decoration(var->self, DecorationPatch);
+	bool is_sample_mask = is_builtin && builtin == BuiltInSampleMask;
 
 
 	// Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it.
 	// Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it.
 	// We must unroll the array load.
 	// We must unroll the array load.
@@ -14964,8 +15070,14 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
 		// The array size might be a specialization constant, so use a for-loop instead.
 		// The array size might be a specialization constant, so use a for-loop instead.
 		statement("for (int i = 0; i < int(", array_expr, "); i++)");
 		statement("for (int i = 0; i < int(", array_expr, "); i++)");
 		begin_scope();
 		begin_scope();
-		if (is_builtin)
+		if (is_builtin && !is_sample_mask)
 			statement(new_expr, "[i] = gl_in[i].", expr, ";");
 			statement(new_expr, "[i] = gl_in[i].", expr, ";");
+		else if (is_sample_mask)
+		{
+			SPIRType target_type;
+			target_type.basetype = SPIRType::Int;
+			statement(new_expr, "[i] = ", bitcast_expression(target_type, type.basetype, join(expr, "[i]")), ";");
+		}
 		else
 		else
 			statement(new_expr, "[i] = ", expr, "[i];");
 			statement(new_expr, "[i] = ", expr, "[i];");
 		end_scope();
 		end_scope();
@@ -14976,6 +15088,10 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
 
 
 void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
 void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
 {
 {
+	// We will handle array cases elsewhere.
+	if (!expr_type.array.empty())
+		return;
+
 	auto *var = maybe_get_backing_variable(source_id);
 	auto *var = maybe_get_backing_variable(source_id);
 	if (var)
 	if (var)
 		source_id = var->self;
 		source_id = var->self;
@@ -15003,6 +15119,7 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
 	case BuiltInDrawIndex:
 	case BuiltInDrawIndex:
 	case BuiltInFragStencilRefEXT:
 	case BuiltInFragStencilRefEXT:
 	case BuiltInInstanceCustomIndexNV:
 	case BuiltInInstanceCustomIndexNV:
+	case BuiltInSampleMask:
 		expected_type = SPIRType::Int;
 		expected_type = SPIRType::Int;
 		break;
 		break;
 
 
@@ -15028,6 +15145,10 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
 
 
 void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
 void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
 {
 {
+	auto *var = maybe_get_backing_variable(target_id);
+	if (var)
+		target_id = var->self;
+
 	// Only interested in standalone builtin variables.
 	// Only interested in standalone builtin variables.
 	if (!has_decoration(target_id, DecorationBuiltIn))
 	if (!has_decoration(target_id, DecorationBuiltIn))
 		return;
 		return;
@@ -15042,6 +15163,7 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
 	case BuiltInPrimitiveId:
 	case BuiltInPrimitiveId:
 	case BuiltInViewportIndex:
 	case BuiltInViewportIndex:
 	case BuiltInFragStencilRefEXT:
 	case BuiltInFragStencilRefEXT:
+	case BuiltInSampleMask:
 		expected_type = SPIRType::Int;
 		expected_type = SPIRType::Int;
 		break;
 		break;
 
 

+ 2 - 0
3rdparty/spirv-cross/spirv_glsl.hpp

@@ -568,6 +568,7 @@ protected:
 		bool support_case_fallthrough = true;
 		bool support_case_fallthrough = true;
 		bool use_array_constructor = false;
 		bool use_array_constructor = false;
 		bool needs_row_major_load_workaround = false;
 		bool needs_row_major_load_workaround = false;
+		bool support_pointer_to_pointer = false;
 	} backend;
 	} backend;
 
 
 	void emit_struct(SPIRType &type);
 	void emit_struct(SPIRType &type);
@@ -879,6 +880,7 @@ protected:
 	virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
 	virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
 	virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
 	virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
 	void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
 	void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
+	bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
 	void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
 	void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
 
 
 	void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
 	void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);

+ 4 - 5
3rdparty/spirv-cross/spirv_hlsl.cpp

@@ -4635,12 +4635,8 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
 		break;
 		break;
 
 
 	case OpGroupNonUniformAllEqual:
 	case OpGroupNonUniformAllEqual:
-	{
-		auto &type = get<SPIRType>(result_type);
-		emit_unary_func_op(result_type, id, ops[3],
-		                   type.basetype == SPIRType::Boolean ? "WaveActiveAllEqualBool" : "WaveActiveAllEqual");
+		emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllEqual");
 		break;
 		break;
-	}
 
 
 	// clang-format off
 	// clang-format off
 #define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \
 #define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \
@@ -4688,6 +4684,9 @@ case OpGroupNonUniform##op: \
 	HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
 	HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
 	HLSL_GROUP_OP(BitwiseOr, BitOr, false)
 	HLSL_GROUP_OP(BitwiseOr, BitOr, false)
 	HLSL_GROUP_OP(BitwiseXor, BitXor, false)
 	HLSL_GROUP_OP(BitwiseXor, BitXor, false)
+	HLSL_GROUP_OP_CAST(LogicalAnd, BitAnd, uint_type)
+	HLSL_GROUP_OP_CAST(LogicalOr, BitOr, uint_type)
+	HLSL_GROUP_OP_CAST(LogicalXor, BitXor, uint_type)
 
 
 #undef HLSL_GROUP_OP
 #undef HLSL_GROUP_OP
 #undef HLSL_GROUP_OP_CAST
 #undef HLSL_GROUP_OP_CAST

+ 304 - 72
3rdparty/spirv-cross/spirv_msl.cpp

@@ -100,7 +100,16 @@ void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bo
 
 
 bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
 bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
 {
 {
-	return inputs_in_use.count(location) != 0;
+	return location_inputs_in_use.count(location) != 0;
+}
+
+uint32_t CompilerMSL::get_automatic_builtin_input_location(spv::BuiltIn builtin) const
+{
+	auto itr = builtin_to_automatic_input_location.find(builtin);
+	if (itr == builtin_to_automatic_input_location.end())
+		return k_unknown_location;
+	else
+		return itr->second;
 }
 }
 
 
 bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
 bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
@@ -390,6 +399,7 @@ void CompilerMSL::build_implicit_builtins()
 				SPIRType vec4_type_ptr;
 				SPIRType vec4_type_ptr;
 				vec4_type_ptr = vec4_type;
 				vec4_type_ptr = vec4_type;
 				vec4_type_ptr.pointer = true;
 				vec4_type_ptr.pointer = true;
+				vec4_type_ptr.pointer_depth++;
 				vec4_type_ptr.parent_type = type_id;
 				vec4_type_ptr.parent_type = type_id;
 				vec4_type_ptr.storage = StorageClassInput;
 				vec4_type_ptr.storage = StorageClassInput;
 				auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
 				auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
@@ -411,6 +421,7 @@ void CompilerMSL::build_implicit_builtins()
 				SPIRType uint_type_ptr;
 				SPIRType uint_type_ptr;
 				uint_type_ptr = get_uint_type();
 				uint_type_ptr = get_uint_type();
 				uint_type_ptr.pointer = true;
 				uint_type_ptr.pointer = true;
+				uint_type_ptr.pointer_depth++;
 				uint_type_ptr.parent_type = get_uint_type_id();
 				uint_type_ptr.parent_type = get_uint_type_id();
 				uint_type_ptr.storage = StorageClassInput;
 				uint_type_ptr.storage = StorageClassInput;
 				auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 				auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -432,6 +443,7 @@ void CompilerMSL::build_implicit_builtins()
 				SPIRType uint_type_ptr;
 				SPIRType uint_type_ptr;
 				uint_type_ptr = get_uint_type();
 				uint_type_ptr = get_uint_type();
 				uint_type_ptr.pointer = true;
 				uint_type_ptr.pointer = true;
+				uint_type_ptr.pointer_depth++;
 				uint_type_ptr.parent_type = get_uint_type_id();
 				uint_type_ptr.parent_type = get_uint_type_id();
 				uint_type_ptr.storage = StorageClassInput;
 				uint_type_ptr.storage = StorageClassInput;
 				auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 				auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -454,6 +466,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -473,6 +486,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -532,6 +546,7 @@ void CompilerMSL::build_implicit_builtins()
 				SPIRType uint_type_ptr_out;
 				SPIRType uint_type_ptr_out;
 				uint_type_ptr_out = get_uint_type();
 				uint_type_ptr_out = get_uint_type();
 				uint_type_ptr_out.pointer = true;
 				uint_type_ptr_out.pointer = true;
+				uint_type_ptr_out.pointer_depth++;
 				uint_type_ptr_out.parent_type = get_uint_type_id();
 				uint_type_ptr_out.parent_type = get_uint_type_id();
 				uint_type_ptr_out.storage = StorageClassOutput;
 				uint_type_ptr_out.storage = StorageClassOutput;
 				auto &ptr_out_type = set<SPIRType>(type_ptr_out_id, uint_type_ptr_out);
 				auto &ptr_out_type = set<SPIRType>(type_ptr_out_id, uint_type_ptr_out);
@@ -563,6 +578,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -622,6 +638,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -643,6 +660,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
 			auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -701,6 +719,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr_out;
 			SPIRType uint_type_ptr_out;
 			uint_type_ptr_out = get_uint_type();
 			uint_type_ptr_out = get_uint_type();
 			uint_type_ptr_out.pointer = true;
 			uint_type_ptr_out.pointer = true;
+			uint_type_ptr_out.pointer_depth++;
 			uint_type_ptr_out.parent_type = get_uint_type_id();
 			uint_type_ptr_out.parent_type = get_uint_type_id();
 			uint_type_ptr_out.storage = StorageClassOutput;
 			uint_type_ptr_out.storage = StorageClassOutput;
 
 
@@ -722,6 +741,7 @@ void CompilerMSL::build_implicit_builtins()
 			SPIRType uint_type_ptr;
 			SPIRType uint_type_ptr;
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr = get_uint_type();
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.parent_type = get_uint_type_id();
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 
 
@@ -743,6 +763,7 @@ void CompilerMSL::build_implicit_builtins()
 			uint32_t type_id = build_extended_vector_type(get_uint_type_id(), 3);
 			uint32_t type_id = build_extended_vector_type(get_uint_type_id(), 3);
 			SPIRType uint_type_ptr = get<SPIRType>(type_id);
 			SPIRType uint_type_ptr = get<SPIRType>(type_id);
 			uint_type_ptr.pointer = true;
 			uint_type_ptr.pointer = true;
+			uint_type_ptr.pointer_depth++;
 			uint_type_ptr.parent_type = type_id;
 			uint_type_ptr.parent_type = type_id;
 			uint_type_ptr.storage = StorageClassInput;
 			uint_type_ptr.storage = StorageClassInput;
 
 
@@ -835,6 +856,7 @@ void CompilerMSL::build_implicit_builtins()
 		SPIRType vec4_type_ptr;
 		SPIRType vec4_type_ptr;
 		vec4_type_ptr = vec4_type;
 		vec4_type_ptr = vec4_type;
 		vec4_type_ptr.pointer = true;
 		vec4_type_ptr.pointer = true;
+		vec4_type_ptr.pointer_depth++;
 		vec4_type_ptr.parent_type = type_id;
 		vec4_type_ptr.parent_type = type_id;
 		vec4_type_ptr.storage = StorageClassOutput;
 		vec4_type_ptr.storage = StorageClassOutput;
 		auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
 		auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
@@ -910,7 +932,7 @@ uint32_t CompilerMSL::build_constant_uint_array_pointer()
 	// Create a buffer to hold extra data, including the swizzle constants.
 	// Create a buffer to hold extra data, including the swizzle constants.
 	SPIRType uint_type_pointer = get_uint_type();
 	SPIRType uint_type_pointer = get_uint_type();
 	uint_type_pointer.pointer = true;
 	uint_type_pointer.pointer = true;
-	uint_type_pointer.pointer_depth = 1;
+	uint_type_pointer.pointer_depth++;
 	uint_type_pointer.parent_type = get_uint_type_id();
 	uint_type_pointer.parent_type = get_uint_type_id();
 	uint_type_pointer.storage = StorageClassUniform;
 	uint_type_pointer.storage = StorageClassUniform;
 	set<SPIRType>(type_ptr_id, uint_type_pointer);
 	set<SPIRType>(type_ptr_id, uint_type_pointer);
@@ -1269,6 +1291,7 @@ string CompilerMSL::compile()
 	backend.array_is_value_type = !msl_options.force_native_arrays;
 	backend.array_is_value_type = !msl_options.force_native_arrays;
 	// Arrays which are part of buffer objects are never considered to be native arrays.
 	// Arrays which are part of buffer objects are never considered to be native arrays.
 	backend.buffer_offset_array_is_value_type = false;
 	backend.buffer_offset_array_is_value_type = false;
+	backend.support_pointer_to_pointer = true;
 
 
 	capture_output_to_buffer = msl_options.capture_output_to_buffer;
 	capture_output_to_buffer = msl_options.capture_output_to_buffer;
 	is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
 	is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
@@ -1730,6 +1753,7 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
 						ptr.self = mbr_type_id;
 						ptr.self = mbr_type_id;
 						ptr.storage = var.storage;
 						ptr.storage = var.storage;
 						ptr.pointer = true;
 						ptr.pointer = true;
+						ptr.pointer_depth++;
 						ptr.parent_type = mbr_type_id;
 						ptr.parent_type = mbr_type_id;
 
 
 						func.add_parameter(mbr_type_id, var_id, true);
 						func.add_parameter(mbr_type_id, var_id, true);
@@ -1801,34 +1825,28 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
 	}
 	}
 }
 }
 
 
+uint32_t CompilerMSL::type_to_location_count(const SPIRType &type) const
+{
+	// In MSL, we cannot place structs in any context where we need locations.
+	assert(type.basetype != SPIRType::Struct);
+
+	uint32_t dim = 1;
+	for (uint32_t i = 0; i < type.array.size(); i++)
+		dim *= to_array_size_literal(type, i);
+
+	uint32_t count = dim * type.columns;
+	return count;
+}
+
 // If a shader input exists at the location, it is marked as being used by this shader
 // If a shader input exists at the location, it is marked as being used by this shader
 void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, StorageClass storage)
 void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, StorageClass storage)
 {
 {
 	if (storage != StorageClassInput)
 	if (storage != StorageClassInput)
 		return;
 		return;
-	if (is_array(type))
-	{
-		uint32_t dim = 1;
-		for (uint32_t i = 0; i < type.array.size(); i++)
-			dim *= to_array_size_literal(type, i);
-		for (uint32_t i = 0; i < dim; i++)
-		{
-			if (is_matrix(type))
-			{
-				for (uint32_t j = 0; j < type.columns; j++)
-					inputs_in_use.insert(location++);
-			}
-			else
-				inputs_in_use.insert(location++);
-		}
-	}
-	else if (is_matrix(type))
-	{
-		for (uint32_t i = 0; i < type.columns; i++)
-			inputs_in_use.insert(location + i);
-	}
-	else
-		inputs_in_use.insert(location);
+
+	uint32_t count = type_to_location_count(type);
+	for (uint32_t i = 0; i < count; i++)
+		location_inputs_in_use.insert(location + i);
 }
 }
 
 
 uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
 uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
@@ -1872,6 +1890,7 @@ uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t comp
 		type->parent_type = new_type_id;
 		type->parent_type = new_type_id;
 		type->storage = old_type.storage;
 		type->storage = old_type.storage;
 		type->pointer = true;
 		type->pointer = true;
+		type->pointer_depth++;
 		new_type_id = ptr_type_id;
 		new_type_id = ptr_type_id;
 	}
 	}
 
 
@@ -2726,7 +2745,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 	// Force the variable to have the proper name.
 	// Force the variable to have the proper name.
 	set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
 	set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
 
 
-	if (get_entry_point().flags.get(ExecutionModeTriangles))
+	if (get_execution_mode_bitset().get(ExecutionModeTriangles))
 	{
 	{
 		// Triangles are tricky, because we want only one member in the struct.
 		// Triangles are tricky, because we want only one member in the struct.
 
 
@@ -2749,6 +2768,10 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 			// Give the member a name
 			// Give the member a name
 			set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
 			set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
 
 
+			// We cannot decorate both, but the important part is that
+			// it's marked as builtin so we can get automatic attribute assignment if needed.
+			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
+
 			// There is no qualified alias since we need to flatten the internal array on return.
 			// There is no qualified alias since we need to flatten the internal array on return.
 			if (get_decoration_bitset(var.self).get(DecorationLocation))
 			if (get_decoration_bitset(var.self).get(DecorationLocation))
 			{
 			{
@@ -2796,6 +2819,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 		uint32_t ptr_type_id = ir.increase_bound_by(1);
 		uint32_t ptr_type_id = ir.increase_bound_by(1);
 		auto &new_var_type = set<SPIRType>(ptr_type_id, get<SPIRType>(type_id));
 		auto &new_var_type = set<SPIRType>(ptr_type_id, get<SPIRType>(type_id));
 		new_var_type.pointer = true;
 		new_var_type.pointer = true;
+		new_var_type.pointer_depth++;
 		new_var_type.storage = StorageClassInput;
 		new_var_type.storage = StorageClassInput;
 		new_var_type.parent_type = type_id;
 		new_var_type.parent_type = type_id;
 		var.basetype = ptr_type_id;
 		var.basetype = ptr_type_id;
@@ -2811,6 +2835,8 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 		string qual_var_name = ib_var_ref + "." + mbr_name;
 		string qual_var_name = ib_var_ref + "." + mbr_name;
 		ir.meta[var.self].decoration.qualified_alias = qual_var_name;
 		ir.meta[var.self].decoration.qualified_alias = qual_var_name;
 
 
+		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
+
 		if (get_decoration_bitset(var.self).get(DecorationLocation))
 		if (get_decoration_bitset(var.self).get(DecorationLocation))
 		{
 		{
 			uint32_t locn = get_decoration(var.self, DecorationLocation);
 			uint32_t locn = get_decoration(var.self, DecorationLocation);
@@ -3301,6 +3327,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
 			set<SPIRType>(array_type_id, type);
 			set<SPIRType>(array_type_id, type);
 
 
 			type.pointer = true;
 			type.pointer = true;
+			type.pointer_depth++;
 			type.parent_type = array_type_id;
 			type.parent_type = array_type_id;
 			type.storage = storage;
 			type.storage = storage;
 			auto &ptr_type = set<SPIRType>(ptr_type_id, type);
 			auto &ptr_type = set<SPIRType>(ptr_type_id, type);
@@ -3356,6 +3383,7 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
 		auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
 		auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
 		ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
 		ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
 		ib_ptr_type.pointer = true;
 		ib_ptr_type.pointer = true;
+		ib_ptr_type.pointer_depth++;
 		ib_ptr_type.storage =
 		ib_ptr_type.storage =
 		    storage == StorageClassInput ?
 		    storage == StorageClassInput ?
 		        (msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) :
 		        (msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) :
@@ -3421,6 +3449,7 @@ uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn buil
 		auto &ptr_type = set<SPIRType>(ptr_type_id);
 		auto &ptr_type = set<SPIRType>(ptr_type_id);
 		ptr_type = base_type;
 		ptr_type = base_type;
 		ptr_type.pointer = true;
 		ptr_type.pointer = true;
+		ptr_type.pointer_depth++;
 		ptr_type.storage = type.storage;
 		ptr_type.storage = type.storage;
 		ptr_type.parent_type = base_type_id;
 		ptr_type.parent_type = base_type_id;
 		return ptr_type_id;
 		return ptr_type_id;
@@ -6088,6 +6117,30 @@ void CompilerMSL::emit_custom_functions()
 	}
 	}
 }
 }
 
 
+static string inject_top_level_storage_qualifier(const string &expr, const string &qualifier)
+{
+	// Easier to do this through text munging since the qualifier does not exist in the type system at all,
+	// and plumbing in all that information is not very helpful.
+	size_t last_reference = expr.find_last_of('&');
+	size_t last_pointer = expr.find_last_of('*');
+	size_t last_significant = string::npos;
+
+	if (last_reference == string::npos)
+		last_significant = last_pointer;
+	else if (last_pointer == string::npos)
+		last_significant = last_reference;
+	else
+		last_significant = std::max(last_reference, last_pointer);
+
+	if (last_significant == string::npos)
+		return join(qualifier, " ", expr);
+	else
+	{
+		return join(expr.substr(0, last_significant + 1), " ",
+		            qualifier, expr.substr(last_significant + 1, string::npos));
+	}
+}
+
 // Undefined global memory is not allowed in MSL.
 // Undefined global memory is not allowed in MSL.
 // Declare constant and init to zeros. Use {}, as global constructors can break Metal.
 // Declare constant and init to zeros. Use {}, as global constructors can break Metal.
 void CompilerMSL::declare_undefined_values()
 void CompilerMSL::declare_undefined_values()
@@ -6099,7 +6152,10 @@ void CompilerMSL::declare_undefined_values()
 		if (type.basetype == SPIRType::Void)
 		if (type.basetype == SPIRType::Void)
 			return;
 			return;
 
 
-		statement("constant ", variable_decl(type, to_name(undef.self), undef.self), " = {};");
+		statement(inject_top_level_storage_qualifier(
+				variable_decl(type, to_name(undef.self), undef.self),
+				"constant"),
+		          " = {};");
 		emitted = true;
 		emitted = true;
 	});
 	});
 
 
@@ -6127,7 +6183,8 @@ void CompilerMSL::declare_constant_arrays()
 		if (!type.array.empty() && (!fully_inlined || is_scalar(type) || is_vector(type)))
 		if (!type.array.empty() && (!fully_inlined || is_scalar(type) || is_vector(type)))
 		{
 		{
 			auto name = to_name(c.self);
 			auto name = to_name(c.self);
-			statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
+			statement(inject_top_level_storage_qualifier(variable_decl(type, name), "constant"),
+			          " = ", constant_expression(c), ";");
 			emitted = true;
 			emitted = true;
 		}
 		}
 	});
 	});
@@ -7997,6 +8054,7 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
 	bool lhs_thread = (lhs_storage == StorageClassOutput || lhs_storage == StorageClassFunction ||
 	bool lhs_thread = (lhs_storage == StorageClassOutput || lhs_storage == StorageClassFunction ||
 	                   lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate);
 	                   lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate);
 	bool rhs_thread = (rhs_storage == StorageClassInput || rhs_storage == StorageClassFunction ||
 	bool rhs_thread = (rhs_storage == StorageClassInput || rhs_storage == StorageClassFunction ||
+	                   rhs_storage == StorageClassOutput ||
 	                   rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate);
 	                   rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate);
 
 
 	// If threadgroup storage qualifiers are *not* used:
 	// If threadgroup storage qualifiers are *not* used:
@@ -8080,6 +8138,14 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
 	}
 	}
 }
 }
 
 
+uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const
+{
+	if (get_execution_mode_bitset().get(ExecutionModeTriangles))
+		return builtin == BuiltInTessLevelInner ? 1 : 3;
+	else
+		return builtin == BuiltInTessLevelInner ? 2 : 4;
+}
+
 // Since MSL does not allow arrays to be copied via simple variable assignment,
 // Since MSL does not allow arrays to be copied via simple variable assignment,
 // if the LHS and RHS represent an assignment of an entire array, it must be
 // if the LHS and RHS represent an assignment of an entire array, it must be
 // implemented by calling an array copy function.
 // implemented by calling an array copy function.
@@ -8108,6 +8174,25 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
 		return true;
 		return true;
 	}
 	}
 
 
+	if (get_execution_model() == ExecutionModelTessellationControl &&
+	    has_decoration(id_lhs, DecorationBuiltIn))
+	{
+		auto builtin = BuiltIn(get_decoration(id_lhs, DecorationBuiltIn));
+		// Need to manually unroll the array store.
+		if (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)
+		{
+			uint32_t array_size = get_physical_tess_level_array_size(builtin);
+			if (array_size == 1)
+				statement(to_expression(id_lhs), " = half(", to_expression(id_rhs), "[0]);");
+			else
+			{
+				for (uint32_t i = 0; i < array_size; i++)
+					statement(to_expression(id_lhs), "[", i, "] = half(", to_expression(id_rhs), "[", i, "]);");
+			}
+			return true;
+		}
+	}
+
 	// Ensure the LHS variable has been declared
 	// Ensure the LHS variable has been declared
 	auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
 	auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
 	if (p_v_lhs)
 	if (p_v_lhs)
@@ -10047,7 +10132,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 				return "";
 				return "";
 			}
 			}
 		}
 		}
-		uint32_t locn = get_ordered_member_location(type.self, index);
+
+		uint32_t locn;
+		if (is_builtin)
+			locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
+		else
+			locn = get_member_location(type.self, index);
+
 		if (locn != k_unknown_location)
 		if (locn != k_unknown_location)
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 	}
 	}
@@ -10087,7 +10178,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 			}
 			}
 		}
 		}
 		uint32_t comp;
 		uint32_t comp;
-		uint32_t locn = get_ordered_member_location(type.self, index, &comp);
+		uint32_t locn = get_member_location(type.self, index, &comp);
 		if (locn != k_unknown_location)
 		if (locn != k_unknown_location)
 		{
 		{
 			if (comp != k_unknown_component)
 			if (comp != k_unknown_component)
@@ -10123,7 +10214,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 		}
 		}
 		if (msl_options.multi_patch_workgroup)
 		if (msl_options.multi_patch_workgroup)
 			return "";
 			return "";
-		uint32_t locn = get_ordered_member_location(type.self, index);
+
+		uint32_t locn;
+		if (is_builtin)
+			locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
+		else
+			locn = get_member_location(type.self, index);
+
 		if (locn != k_unknown_location)
 		if (locn != k_unknown_location)
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 	}
 	}
@@ -10156,7 +10253,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 		// The special control point array must not be marked with an attribute.
 		// The special control point array must not be marked with an attribute.
 		if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
 		if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
 			return "";
 			return "";
-		uint32_t locn = get_ordered_member_location(type.self, index);
+
+		uint32_t locn;
+		if (is_builtin)
+			locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
+		else
+			locn = get_member_location(type.self, index);
+
 		if (locn != k_unknown_location)
 		if (locn != k_unknown_location)
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 			return string(" [[attribute(") + convert_to_string(locn) + ")]]";
 	}
 	}
@@ -10196,7 +10299,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 		else
 		else
 		{
 		{
 			uint32_t comp;
 			uint32_t comp;
-			uint32_t locn = get_ordered_member_location(type.self, index, &comp);
+			uint32_t locn = get_member_location(type.self, index, &comp);
 			if (locn != k_unknown_location)
 			if (locn != k_unknown_location)
 			{
 			{
 				// For user-defined attributes, this is fine. From Vulkan spec:
 				// For user-defined attributes, this is fine. From Vulkan spec:
@@ -10298,7 +10401,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 				return "";
 				return "";
 			}
 			}
 		}
 		}
-		uint32_t locn = get_ordered_member_location(type.self, index);
+		uint32_t locn = get_member_location(type.self, index);
 		// Metal will likely complain about missing color attachments, too.
 		// Metal will likely complain about missing color attachments, too.
 		if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn)))
 		if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn)))
 			return "";
 			return "";
@@ -10347,24 +10450,61 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
 // If the location of the member has been explicitly set, that location is used. If not, this
 // If the location of the member has been explicitly set, that location is used. If not, this
 // function assumes the members are ordered in their location order, and simply returns the
 // function assumes the members are ordered in their location order, and simply returns the
 // index as the location.
 // index as the location.
-uint32_t CompilerMSL::get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp)
+uint32_t CompilerMSL::get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp) const
 {
 {
-	auto &m = ir.meta[type_id];
-	if (index < m.members.size())
+	if (comp)
 	{
 	{
-		auto &dec = m.members[index];
-		if (comp)
-		{
-			if (dec.decoration_flags.get(DecorationComponent))
-				*comp = dec.component;
-			else
-				*comp = k_unknown_component;
-		}
-		if (dec.decoration_flags.get(DecorationLocation))
-			return dec.location;
+		if (has_member_decoration(type_id, index, DecorationComponent))
+			*comp = get_member_decoration(type_id, index, DecorationComponent);
+		else
+			*comp = k_unknown_component;
 	}
 	}
 
 
-	return index;
+	if (has_member_decoration(type_id, index, DecorationLocation))
+		return get_member_decoration(type_id, index, DecorationLocation);
+	else
+		return k_unknown_location;
+}
+
+uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
+                                                                    uint32_t type_id, uint32_t index,
+                                                                    uint32_t *comp)
+{
+	uint32_t loc = get_member_location(type_id, index, comp);
+	if (loc != k_unknown_location)
+		return loc;
+
+	if (comp)
+		*comp = k_unknown_component;
+
+	// Late allocation. Find a location which is unused by the application.
+	// This can happen for built-in inputs in tessellation which are mixed and matched with user inputs.
+	auto &mbr_type = get<SPIRType>(get<SPIRType>(type_id).member_types[index]);
+	uint32_t count = type_to_location_count(mbr_type);
+
+	// This should always be 1.
+	if (count != 1)
+		return k_unknown_location;
+
+	loc = 0;
+	while (location_inputs_in_use.count(loc) != 0)
+		loc++;
+
+	set_member_decoration(type_id, index, DecorationLocation, loc);
+
+	// Triangle tess level inputs are shared in one packed float4,
+	// mark both builtins as sharing one location.
+	if (get_execution_mode_bitset().get(ExecutionModeTriangles) &&
+	    (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
+	{
+		builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc;
+		builtin_to_automatic_input_location[BuiltInTessLevelOuter] = loc;
+	}
+	else
+		builtin_to_automatic_input_location[builtin] = loc;
+
+	mark_location_as_used_by_shader(loc, mbr_type, StorageClassInput);
+	return loc;
 }
 }
 
 
 // Returns the type declaration for a function, including the
 // Returns the type declaration for a function, including the
@@ -10508,8 +10648,10 @@ string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bo
 	}
 	}
 
 
 	if (!addr_space)
 	if (!addr_space)
+	{
 		// No address space for plain values.
 		// No address space for plain values.
 		addr_space = type.pointer || (argument && type.basetype == SPIRType::ControlPointArray) ? "thread" : "";
 		addr_space = type.pointer || (argument && type.basetype == SPIRType::ControlPointArray) ? "thread" : "";
+	}
 
 
 	return join(flags.get(DecorationVolatile) || flags.get(DecorationCoherent) ? "volatile " : "", addr_space);
 	return join(flags.get(DecorationVolatile) || flags.get(DecorationCoherent) ? "volatile " : "", addr_space);
 }
 }
@@ -11838,6 +11980,24 @@ bool CompilerMSL::type_is_msl_framebuffer_fetch(const SPIRType &type) const
 	       msl_options.use_framebuffer_fetch_subpasses;
 	       msl_options.use_framebuffer_fetch_subpasses;
 }
 }
 
 
+bool CompilerMSL::type_is_pointer(const SPIRType &type) const
+{
+	if (!type.pointer)
+		return false;
+	auto &parent_type = get<SPIRType>(type.parent_type);
+	// Safeguards when we forget to set pointer_depth (there is an assert for it in type_to_glsl),
+	// but the extra check shouldn't hurt.
+	return (type.pointer_depth > parent_type.pointer_depth) || !parent_type.pointer;
+}
+
+bool CompilerMSL::type_is_pointer_to_pointer(const SPIRType &type) const
+{
+	if (!type.pointer)
+		return false;
+	auto &parent_type = get<SPIRType>(type.parent_type);
+	return type.pointer_depth > parent_type.pointer_depth && type_is_pointer(parent_type);
+}
+
 string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 {
 {
 	auto &var = get<SPIRVariable>(arg.id);
 	auto &var = get<SPIRVariable>(arg.id);
@@ -11864,9 +12024,8 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 	if (!type.array.empty() && type_is_image)
 	if (!type.array.empty() && type_is_image)
 		constref = true;
 		constref = true;
 
 
+	const char *cv_qualifier = constref ? "const " : "";
 	string decl;
 	string decl;
-	if (constref)
-		decl += "const ";
 
 
 	// If this is a combined image-sampler for a 2D image with floating-point type,
 	// If this is a combined image-sampler for a 2D image with floating-point type,
 	// we emitted the 'spvDynamicImageSampler' type, and this is *not* an alias parameter
 	// we emitted the 'spvDynamicImageSampler' type, and this is *not* an alias parameter
@@ -11880,27 +12039,38 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 	// Allow Metal to use the array<T> template to make arrays a value type
 	// Allow Metal to use the array<T> template to make arrays a value type
 	string address_space = get_argument_address_space(var);
 	string address_space = get_argument_address_space(var);
 	bool builtin = is_builtin_variable(var);
 	bool builtin = is_builtin_variable(var);
+	auto builtin_type = BuiltIn(get_decoration(arg.id, DecorationBuiltIn));
 	is_using_builtin_array = builtin;
 	is_using_builtin_array = builtin;
 	if (address_space == "threadgroup")
 	if (address_space == "threadgroup")
 		is_using_builtin_array = true;
 		is_using_builtin_array = true;
 
 
 	if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id))
 	if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id))
-		decl += type_to_glsl(type, arg.id);
+		decl = join(cv_qualifier, type_to_glsl(type, arg.id));
 	else if (builtin)
 	else if (builtin)
-		decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id);
+		decl = join(cv_qualifier, builtin_type_decl(builtin_type, arg.id));
 	else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
 	else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
 	{
 	{
 		is_using_builtin_array = true;
 		is_using_builtin_array = true;
-		decl += join(type_to_glsl(type, arg.id), "*");
+		decl += join(cv_qualifier, type_to_glsl(type, arg.id), "*");
 	}
 	}
 	else if (is_dynamic_img_sampler)
 	else if (is_dynamic_img_sampler)
 	{
 	{
-		decl += join("spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">");
+		decl = join(cv_qualifier, "spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">");
 		// Mark the variable so that we can handle passing it to another function.
 		// Mark the variable so that we can handle passing it to another function.
 		set_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler);
 		set_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler);
 	}
 	}
 	else
 	else
-		decl += type_to_glsl(type, arg.id);
+	{
+		// The type is a pointer type we need to emit cv_qualifier late.
+		if (type_is_pointer(type))
+		{
+			decl = type_to_glsl(type, arg.id);
+			if (*cv_qualifier != '\0')
+				decl += join(" ", cv_qualifier);
+		}
+		else
+			decl = join(cv_qualifier, type_to_glsl(type, arg.id));
+	}
 
 
 	bool opaque_handle = storage == StorageClassUniformConstant;
 	bool opaque_handle = storage == StorageClassUniformConstant;
 
 
@@ -11968,16 +12138,37 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 			}
 			}
 		}
 		}
 
 
-		decl += " (&";
-		const char *restrict_kw = to_restrict(name_id);
-		if (*restrict_kw)
+		// Special case, need to override the array size here if we're using tess level as an argument.
+		if (get_execution_model() == ExecutionModelTessellationControl && builtin &&
+		    (builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter))
 		{
 		{
-			decl += " ";
-			decl += restrict_kw;
+			uint32_t array_size = get_physical_tess_level_array_size(builtin_type);
+			if (array_size == 1)
+			{
+				decl += " &";
+				decl += to_expression(name_id);
+			}
+			else
+			{
+				decl += " (&";
+				decl += to_expression(name_id);
+				decl += ")";
+				decl += join("[", array_size, "]");
+			}
+		}
+		else
+		{
+			decl += " (&";
+			const char *restrict_kw = to_restrict(name_id);
+			if (*restrict_kw)
+			{
+				decl += " ";
+				decl += restrict_kw;
+			}
+			decl += to_expression(name_id);
+			decl += ")";
+			decl += type_to_array_glsl(type);
 		}
 		}
-		decl += to_expression(name_id);
-		decl += ")";
-		decl += type_to_array_glsl(type);
 	}
 	}
 	else if (!opaque_handle && (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct))
 	else if (!opaque_handle && (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct))
 	{
 	{
@@ -11985,8 +12176,12 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
 		// for the reference has to go before the '&', but after the '*'.
 		// for the reference has to go before the '&', but after the '*'.
 		if (!address_space.empty())
 		if (!address_space.empty())
 		{
 		{
-			if (decl.back() == '*')
-				decl += join(" ", address_space, " ");
+			if (type_is_pointer(type))
+			{
+				if (*cv_qualifier == '\0')
+					decl += ' ';
+				decl += join(address_space, " ");
+			}
 			else
 			else
 				decl = join(address_space, " ", decl);
 				decl = join(address_space, " ", decl);
 		}
 		}
@@ -12434,8 +12629,22 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
 	// Pointer?
 	// Pointer?
 	if (type.pointer)
 	if (type.pointer)
 	{
 	{
+		assert(type.pointer_depth > 0);
+
 		const char *restrict_kw;
 		const char *restrict_kw;
-		type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get<SPIRType>(type.parent_type), id));
+
+		auto type_address_space = get_type_address_space(type, id);
+		auto type_decl = type_to_glsl(get<SPIRType>(type.parent_type), id);
+
+		// Work around C pointer qualifier rules. If glsl_type is a pointer type as well
+		// we'll need to emit the address space to the right.
+		// We could always go this route, but it makes the code unnatural.
+		// Prefer emitting thread T *foo over T thread* foo since it's more readable,
+		// but we'll have to emit thread T * thread * T constant bar; for example.
+		if (type_is_pointer_to_pointer(type))
+			type_name = join(type_decl, " ", type_address_space, " ");
+		else
+			type_name = join(type_address_space, " ", type_decl);
 
 
 		switch (type.basetype)
 		switch (type.basetype)
 		{
 		{
@@ -14531,14 +14740,36 @@ void CompilerMSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
 
 
 	if (expected_type != expr_type.basetype)
 	if (expected_type != expr_type.basetype)
 	{
 	{
-		if (expected_width != expr_type.width)
+		if (!expr_type.array.empty() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
 		{
 		{
-			// These are of different widths, so we cannot do a straight bitcast.
-			expr = join(type_to_glsl(expr_type), "(", expr, ")");
+			// Triggers when loading TessLevel directly as an array.
+			// Need explicit padding + cast.
+			auto wrap_expr = join(type_to_glsl(expr_type), "({ ");
+
+			uint32_t array_size = get_physical_tess_level_array_size(builtin);
+			for (uint32_t i = 0; i < array_size; i++)
+			{
+				if (array_size > 1)
+					wrap_expr += join("float(", expr, "[", i, "])");
+				else
+					wrap_expr += join("float(", expr, ")");
+				if (i + 1 < array_size)
+					wrap_expr += ", ";
+			}
+
+			if (get_execution_mode_bitset().get(ExecutionModeTriangles))
+				wrap_expr += ", 0.0";
+
+			wrap_expr += " })";
+			expr = std::move(wrap_expr);
 		}
 		}
 		else
 		else
 		{
 		{
-			expr = bitcast_expression(expr_type, expected_type, expr);
+			// These are of different widths, so we cannot do a straight bitcast.
+			if (expected_width != expr_type.width)
+				expr = join(type_to_glsl(expr_type), "(", expr, ")");
+			else
+				expr = bitcast_expression(expr_type, expected_type, expr);
 		}
 		}
 	}
 	}
 
 
@@ -14776,7 +15007,7 @@ void CompilerMSL::analyze_argument_buffers()
 				// Create a buffer to hold extra data, including the swizzle constants.
 				// Create a buffer to hold extra data, including the swizzle constants.
 				SPIRType uint_type_pointer = get_uint_type();
 				SPIRType uint_type_pointer = get_uint_type();
 				uint_type_pointer.pointer = true;
 				uint_type_pointer.pointer = true;
-				uint_type_pointer.pointer_depth = 1;
+				uint_type_pointer.pointer_depth++;
 				uint_type_pointer.parent_type = get_uint_type_id();
 				uint_type_pointer.parent_type = get_uint_type_id();
 				uint_type_pointer.storage = StorageClassUniform;
 				uint_type_pointer.storage = StorageClassUniform;
 				set<SPIRType>(uint_ptr_type_id, uint_type_pointer);
 				set<SPIRType>(uint_ptr_type_id, uint_type_pointer);
@@ -14850,7 +15081,7 @@ void CompilerMSL::analyze_argument_buffers()
 		auto &ptr_type = set<SPIRType>(ptr_type_id);
 		auto &ptr_type = set<SPIRType>(ptr_type_id);
 		ptr_type = buffer_type;
 		ptr_type = buffer_type;
 		ptr_type.pointer = true;
 		ptr_type.pointer = true;
-		ptr_type.pointer_depth = 1;
+		ptr_type.pointer_depth++;
 		ptr_type.parent_type = type_id;
 		ptr_type.parent_type = type_id;
 
 
 		uint32_t buffer_variable_id = next_id;
 		uint32_t buffer_variable_id = next_id;
@@ -14937,6 +15168,7 @@ void CompilerMSL::analyze_argument_buffers()
 					set<SPIRType>(atomic_type_id, atomic_type);
 					set<SPIRType>(atomic_type_id, atomic_type);
 
 
 					atomic_type.pointer = true;
 					atomic_type.pointer = true;
+					atomic_type.pointer_depth++;
 					atomic_type.parent_type = atomic_type_id;
 					atomic_type.parent_type = atomic_type_id;
 					atomic_type.storage = StorageClassStorageBuffer;
 					atomic_type.storage = StorageClassStorageBuffer;
 					auto &atomic_ptr_type = set<SPIRType>(type_ptr_id, atomic_type);
 					auto &atomic_ptr_type = set<SPIRType>(type_ptr_id, atomic_type);

+ 18 - 2
3rdparty/spirv-cross/spirv_msl.hpp

@@ -550,6 +550,13 @@ public:
 	// Query after compilation is done. This allows you to check if an input location was used by the shader.
 	// Query after compilation is done. This allows you to check if an input location was used by the shader.
 	bool is_msl_shader_input_used(uint32_t location);
 	bool is_msl_shader_input_used(uint32_t location);
 
 
+	// If not using add_msl_shader_input, it's possible
+	// that certain builtin attributes need to be automatically assigned locations.
+	// This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc.
+	// This returns k_unknown_location if the location was explicitly assigned with
+	// add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]].
+	uint32_t get_automatic_builtin_input_location(spv::BuiltIn builtin) const;
+
 	// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
 	// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
 	// Constexpr samplers are always assumed to be emitted.
 	// Constexpr samplers are always assumed to be emitted.
 	// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
 	// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
@@ -567,6 +574,7 @@ public:
 
 
 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the
 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the
 	// sampler's binding is returned instead. For any other resource type, -1 is returned.
 	// sampler's binding is returned instead. For any other resource type, -1 is returned.
+	// Secondary bindings are also used for the auxillary image atomic buffer.
 	uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
 	uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
 
 
 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images,
 	// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images,
@@ -825,7 +833,11 @@ protected:
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 	std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
 	std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
 	uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
 	uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
-	uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
+	uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const;
+	uint32_t get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
+	                                                       uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
+
+	uint32_t get_physical_tess_level_array_size(spv::BuiltIn builtin) const;
 
 
 	// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
 	// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
 	// These values can change depending on various extended decorations which control packing rules.
 	// These values can change depending on various extended decorations which control packing rules.
@@ -928,13 +940,15 @@ protected:
 	// Must be ordered to ensure declarations are in a specific order.
 	// Must be ordered to ensure declarations are in a specific order.
 	std::map<uint32_t, MSLShaderInput> inputs_by_location;
 	std::map<uint32_t, MSLShaderInput> inputs_by_location;
 	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
 	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
-	std::unordered_set<uint32_t> inputs_in_use;
+	std::unordered_set<uint32_t> location_inputs_in_use;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
+	std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> typedef_lines;
 	std::set<std::string> typedef_lines;
 	SmallVector<uint32_t> vars_needing_early_declaration;
 	SmallVector<uint32_t> vars_needing_early_declaration;
 
 
 	std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
 	std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
+	uint32_t type_to_location_count(const SPIRType &type) const;
 
 
 	uint32_t next_metal_resource_index_buffer = 0;
 	uint32_t next_metal_resource_index_buffer = 0;
 	uint32_t next_metal_resource_index_texture = 0;
 	uint32_t next_metal_resource_index_texture = 0;
@@ -1026,6 +1040,8 @@ protected:
 	void activate_argument_buffer_resources();
 	void activate_argument_buffer_resources();
 
 
 	bool type_is_msl_framebuffer_fetch(const SPIRType &type) const;
 	bool type_is_msl_framebuffer_fetch(const SPIRType &type) const;
+	bool type_is_pointer(const SPIRType &type) const;
+	bool type_is_pointer_to_pointer(const SPIRType &type) const;
 	bool is_supported_argument_buffer_type(const SPIRType &type) const;
 	bool is_supported_argument_buffer_type(const SPIRType &type) const;
 
 
 	// OpcodeHandler that handles several MSL preprocessing operations.
 	// OpcodeHandler that handles several MSL preprocessing operations.

+ 2 - 0
3rdparty/spirv-cross/spirv_parser.cpp

@@ -140,6 +140,8 @@ void Parser::parse()
 		SPIRV_CROSS_THROW("Function was not terminated.");
 		SPIRV_CROSS_THROW("Function was not terminated.");
 	if (current_block)
 	if (current_block)
 		SPIRV_CROSS_THROW("Block was not terminated.");
 		SPIRV_CROSS_THROW("Block was not terminated.");
+	if (ir.default_entry_point == 0)
+		SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module.");
 }
 }
 
 
 const uint32_t *Parser::stream(const Instruction &instr) const
 const uint32_t *Parser::stream(const Instruction &instr) const