Browse Source

Updated spirv-cross.

Бранимир Караџић 2 years ago
parent
commit
709d33f2c5

+ 610 - 91
3rdparty/spirv-cross/spirv_glsl.cpp

@@ -636,6 +636,17 @@ void CompilerGLSL::find_static_extensions()
 			barycentric_is_nv = true;
 			barycentric_is_nv = true;
 }
 }
 
 
+void CompilerGLSL::require_polyfill(Polyfill polyfill, bool relaxed)
+{
+	uint32_t &polyfills = (relaxed && options.es) ? required_polyfills_relaxed : required_polyfills;
+
+	if ((polyfills & polyfill) == 0)
+	{
+		polyfills |= polyfill;
+		force_recompile();
+	}
+}
+
 void CompilerGLSL::ray_tracing_khr_fixup_locations()
 void CompilerGLSL::ray_tracing_khr_fixup_locations()
 {
 {
 	uint32_t location = 0;
 	uint32_t location = 0;
@@ -701,6 +712,11 @@ string CompilerGLSL::compile()
 		emit_resources();
 		emit_resources();
 		emit_extension_workarounds(get_execution_model());
 		emit_extension_workarounds(get_execution_model());
 
 
+		if (required_polyfills != 0)
+			emit_polyfills(required_polyfills, false);
+		if (options.es && required_polyfills_relaxed != 0)
+			emit_polyfills(required_polyfills_relaxed, true);
+
 		emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
 		emit_function(get<SPIRFunction>(ir.default_entry_point), Bitset());
 
 
 		pass_count++;
 		pass_count++;
@@ -1249,14 +1265,33 @@ string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags)
 	if (flags.get(DecorationFlat))
 	if (flags.get(DecorationFlat))
 		res += "flat ";
 		res += "flat ";
 	if (flags.get(DecorationNoPerspective))
 	if (flags.get(DecorationNoPerspective))
+	{
+		if (options.es)
+		{
+			if (options.version < 300)
+				SPIRV_CROSS_THROW("noperspective requires ESSL 300.");
+			require_extension_internal("GL_NV_shader_noperspective_interpolation");
+		}
+		else if (is_legacy_desktop())
+			require_extension_internal("GL_EXT_gpu_shader4");
 		res += "noperspective ";
 		res += "noperspective ";
+	}
 	if (flags.get(DecorationCentroid))
 	if (flags.get(DecorationCentroid))
 		res += "centroid ";
 		res += "centroid ";
 	if (flags.get(DecorationPatch))
 	if (flags.get(DecorationPatch))
 		res += "patch ";
 		res += "patch ";
 	if (flags.get(DecorationSample))
 	if (flags.get(DecorationSample))
+	{
+		if (options.es)
+		{
+			if (options.version < 300)
+				SPIRV_CROSS_THROW("sample requires ESSL 300.");
+			else if (options.version < 320)
+				require_extension_internal("GL_OES_shader_multisample_interpolation");
+		}
 		res += "sample ";
 		res += "sample ";
-	if (flags.get(DecorationInvariant))
+	}
+	if (flags.get(DecorationInvariant) && (options.es || options.version >= 120))
 		res += "invariant ";
 		res += "invariant ";
 	if (flags.get(DecorationPerPrimitiveEXT))
 	if (flags.get(DecorationPerPrimitiveEXT))
 	    res += "perprimitiveEXT ";
 	    res += "perprimitiveEXT ";
@@ -1529,7 +1564,7 @@ uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, const Bits
 
 
 		// In std140, struct alignment is rounded up to 16.
 		// In std140, struct alignment is rounded up to 16.
 		if (packing_is_vec4_padded(packing))
 		if (packing_is_vec4_padded(packing))
-			alignment = max(alignment, 16u);
+			alignment = max<uint32_t>(alignment, 16u);
 
 
 		return alignment;
 		return alignment;
 	}
 	}
@@ -1757,7 +1792,7 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
 			uint32_t begin_word = offset / 16;
 			uint32_t begin_word = offset / 16;
 			uint32_t end_word = (offset + packed_size - 1) / 16;
 			uint32_t end_word = (offset + packed_size - 1) / 16;
 			if (begin_word != end_word)
 			if (begin_word != end_word)
-				packed_alignment = max(packed_alignment, 16u);
+				packed_alignment = max<uint32_t>(packed_alignment, 16u);
 		}
 		}
 
 
 		uint32_t actual_offset = type_struct_member_offset(type, i);
 		uint32_t actual_offset = type_struct_member_offset(type, i);
@@ -2713,30 +2748,26 @@ void CompilerGLSL::emit_interface_block(const SPIRVariable &var)
 		{
 		{
 			add_resource_name(var.self);
 			add_resource_name(var.self);
 
 
-			// Tessellation control and evaluation shaders must have either gl_MaxPatchVertices or unsized arrays for input arrays.
-			// Opt for unsized as it's the more "correct" variant to use.
-			bool control_point_input_array = type.storage == StorageClassInput && !type.array.empty() &&
-			                                 !has_decoration(var.self, DecorationPatch) &&
-			                                 (get_entry_point().model == ExecutionModelTessellationControl ||
-			                                  get_entry_point().model == ExecutionModelTessellationEvaluation);
-
-			uint32_t old_array_size = 0;
-			bool old_array_size_literal = true;
+			// Legacy GLSL did not support int attributes, we automatically
+			// declare them as float and cast them on load/store
+			SPIRType newtype = type;
+			if (is_legacy() && var.storage == StorageClassInput && type.basetype == SPIRType::Int)
+				newtype.basetype = SPIRType::Float;
 
 
-			if (control_point_input_array)
+			// Tessellation control and evaluation shaders must have either
+			// gl_MaxPatchVertices or unsized arrays for input arrays.
+			// Opt for unsized as it's the more "correct" variant to use.
+			if (type.storage == StorageClassInput && !type.array.empty() &&
+			    !has_decoration(var.self, DecorationPatch) &&
+			    (get_entry_point().model == ExecutionModelTessellationControl ||
+			     get_entry_point().model == ExecutionModelTessellationEvaluation))
 			{
 			{
-				swap(type.array.back(), old_array_size);
-				swap(type.array_size_literal.back(), old_array_size_literal);
+				newtype.array.back() = 0;
+				newtype.array_size_literal.back() = true;
 			}
 			}
 
 
 			statement(layout_for_variable(var), to_qualifiers_glsl(var.self),
 			statement(layout_for_variable(var), to_qualifiers_glsl(var.self),
-			          variable_decl(type, to_name(var.self), var.self), ";");
-
-			if (control_point_input_array)
-			{
-				swap(type.array.back(), old_array_size);
-				swap(type.array_size_literal.back(), old_array_size_literal);
-			}
+			          variable_decl(newtype, to_name(var.self), var.self), ";");
 		}
 		}
 	}
 	}
 }
 }
@@ -3511,7 +3542,7 @@ void CompilerGLSL::emit_resources()
 			statement("");
 			statement("");
 	}
 	}
 
 
-	if (position_invariant)
+	if (position_invariant && (options.es || options.version >= 120))
 	{
 	{
 		statement("invariant gl_Position;");
 		statement("invariant gl_Position;");
 		statement("");
 		statement("");
@@ -4386,34 +4417,135 @@ void CompilerGLSL::emit_extension_workarounds(spv::ExecutionModel model)
 		}
 		}
 		statement("");
 		statement("");
 	}
 	}
+}
+
+void CompilerGLSL::emit_polyfills(uint32_t polyfills, bool relaxed)
+{
+	const char *qual = "";
+	const char *suffix = (options.es && relaxed) ? "MP" : "";
+	if (options.es)
+		qual = relaxed ? "mediump " : "highp ";
 
 
-	if (requires_transpose_2x2)
+	if (polyfills & PolyfillTranspose2x2)
 	{
 	{
-		statement("mat2 spvTranspose(mat2 m)");
+		statement(qual, "mat2 spvTranspose", suffix, "(", qual, "mat2 m)");
 		begin_scope();
 		begin_scope();
 		statement("return mat2(m[0][0], m[1][0], m[0][1], m[1][1]);");
 		statement("return mat2(m[0][0], m[1][0], m[0][1], m[1][1]);");
 		end_scope();
 		end_scope();
 		statement("");
 		statement("");
 	}
 	}
 
 
-	if (requires_transpose_3x3)
+	if (polyfills & PolyfillTranspose3x3)
 	{
 	{
-		statement("mat3 spvTranspose(mat3 m)");
+		statement(qual, "mat3 spvTranspose", suffix, "(", qual, "mat3 m)");
 		begin_scope();
 		begin_scope();
 		statement("return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);");
 		statement("return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);");
 		end_scope();
 		end_scope();
 		statement("");
 		statement("");
 	}
 	}
 
 
-	if (requires_transpose_4x4)
+	if (polyfills & PolyfillTranspose4x4)
 	{
 	{
-		statement("mat4 spvTranspose(mat4 m)");
+		statement(qual, "mat4 spvTranspose", suffix, "(", qual, "mat4 m)");
 		begin_scope();
 		begin_scope();
 		statement("return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], "
 		statement("return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], "
 		          "m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]);");
 		          "m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]);");
 		end_scope();
 		end_scope();
 		statement("");
 		statement("");
 	}
 	}
+
+	if (polyfills & PolyfillDeterminant2x2)
+	{
+		statement(qual, "float spvDeterminant", suffix, "(", qual, "mat2 m)");
+		begin_scope();
+		statement("return m[0][0] * m[1][1] - m[0][1] * m[1][0];");
+		end_scope();
+		statement("");
+	}
+
+	if (polyfills & PolyfillDeterminant3x3)
+	{
+		statement(qual, "float spvDeterminant", suffix, "(", qual, "mat3 m)");
+		begin_scope();
+		statement("return dot(m[0], vec3(m[1][1] * m[2][2] - m[1][2] * m[2][1], "
+		                                "m[1][2] * m[2][0] - m[1][0] * m[2][2], "
+		                                "m[1][0] * m[2][1] - m[1][1] * m[2][0]));");
+		end_scope();
+		statement("");
+	}
+
+	if (polyfills & PolyfillDeterminant4x4)
+	{
+		statement(qual, "float spvDeterminant", suffix, "(", qual, "mat4 m)");
+		begin_scope();
+		statement("return dot(m[0], vec4("
+		          "m[2][1] * m[3][2] * m[1][3] - m[3][1] * m[2][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3] - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] + m[1][1] * m[2][2] * m[3][3], "
+		          "m[3][0] * m[2][2] * m[1][3] - m[2][0] * m[3][2] * m[1][3] - m[3][0] * m[1][2] * m[2][3] + m[1][0] * m[3][2] * m[2][3] + m[2][0] * m[1][2] * m[3][3] - m[1][0] * m[2][2] * m[3][3], "
+		          "m[2][0] * m[3][1] * m[1][3] - m[3][0] * m[2][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3] - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] + m[1][0] * m[2][1] * m[3][3], "
+		          "m[3][0] * m[2][1] * m[1][2] - m[2][0] * m[3][1] * m[1][2] - m[3][0] * m[1][1] * m[2][2] + m[1][0] * m[3][1] * m[2][2] + m[2][0] * m[1][1] * m[3][2] - m[1][0] * m[2][1] * m[3][2]));");
+		end_scope();
+		statement("");
+	}
+
+	if (polyfills & PolyfillMatrixInverse2x2)
+	{
+		statement(qual, "mat2 spvInverse", suffix, "(", qual, "mat2 m)");
+		begin_scope();
+		statement("return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) "
+		          "* (1.0 / (m[0][0] * m[1][1] - m[1][0] * m[0][1]));");
+		end_scope();
+		statement("");
+	}
+
+	if (polyfills & PolyfillMatrixInverse3x3)
+	{
+		statement(qual, "mat3 spvInverse", suffix, "(", qual, "mat3 m)");
+		begin_scope();
+		statement(qual, "vec3 t = vec3(m[1][1] * m[2][2] - m[1][2] * m[2][1], m[1][2] * m[2][0] - m[1][0] * m[2][2], m[1][0] * m[2][1] - m[1][1] * m[2][0]);");
+		statement("return mat3(t[0], "
+		                      "m[0][2] * m[2][1] - m[0][1] * m[2][2], "
+		                      "m[0][1] * m[1][2] - m[0][2] * m[1][1], "
+		                      "t[1], "
+		                      "m[0][0] * m[2][2] - m[0][2] * m[2][0], "
+		                      "m[0][2] * m[1][0] - m[0][0] * m[1][2], "
+		                      "t[2], "
+		                      "m[0][1] * m[2][0] - m[0][0] * m[2][1], "
+		                      "m[0][0] * m[1][1] - m[0][1] * m[1][0]) "
+		                      "* (1.0 / dot(m[0], t));");
+		end_scope();
+		statement("");
+	}
+
+	if (polyfills & PolyfillMatrixInverse4x4)
+	{
+		statement(qual, "mat4 spvInverse", suffix, "(", qual, "mat4 m)");
+		begin_scope();
+		statement(qual, "vec4 t = vec4("
+		          "m[2][1] * m[3][2] * m[1][3] - m[3][1] * m[2][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3] - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] + m[1][1] * m[2][2] * m[3][3], "
+		          "m[3][0] * m[2][2] * m[1][3] - m[2][0] * m[3][2] * m[1][3] - m[3][0] * m[1][2] * m[2][3] + m[1][0] * m[3][2] * m[2][3] + m[2][0] * m[1][2] * m[3][3] - m[1][0] * m[2][2] * m[3][3], "
+		          "m[2][0] * m[3][1] * m[1][3] - m[3][0] * m[2][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3] - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] + m[1][0] * m[2][1] * m[3][3], "
+		          "m[3][0] * m[2][1] * m[1][2] - m[2][0] * m[3][1] * m[1][2] - m[3][0] * m[1][1] * m[2][2] + m[1][0] * m[3][1] * m[2][2] + m[2][0] * m[1][1] * m[3][2] - m[1][0] * m[2][1] * m[3][2]);");
+		statement("return mat4("
+		          "t[0], "
+		          "m[3][1] * m[2][2] * m[0][3] - m[2][1] * m[3][2] * m[0][3] - m[3][1] * m[0][2] * m[2][3] + m[0][1] * m[3][2] * m[2][3] + m[2][1] * m[0][2] * m[3][3] - m[0][1] * m[2][2] * m[3][3], "
+		          "m[1][1] * m[3][2] * m[0][3] - m[3][1] * m[1][2] * m[0][3] + m[3][1] * m[0][2] * m[1][3] - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] + m[0][1] * m[1][2] * m[3][3], "
+		          "m[2][1] * m[1][2] * m[0][3] - m[1][1] * m[2][2] * m[0][3] - m[2][1] * m[0][2] * m[1][3] + m[0][1] * m[2][2] * m[1][3] + m[1][1] * m[0][2] * m[2][3] - m[0][1] * m[1][2] * m[2][3], "
+		          "t[1], "
+		          "m[2][0] * m[3][2] * m[0][3] - m[3][0] * m[2][2] * m[0][3] + m[3][0] * m[0][2] * m[2][3] - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] + m[0][0] * m[2][2] * m[3][3], "
+		          "m[3][0] * m[1][2] * m[0][3] - m[1][0] * m[3][2] * m[0][3] - m[3][0] * m[0][2] * m[1][3] + m[0][0] * m[3][2] * m[1][3] + m[1][0] * m[0][2] * m[3][3] - m[0][0] * m[1][2] * m[3][3], "
+		          "m[1][0] * m[2][2] * m[0][3] - m[2][0] * m[1][2] * m[0][3] + m[2][0] * m[0][2] * m[1][3] - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] + m[0][0] * m[1][2] * m[2][3], "
+		          "t[2], "
+		          "m[3][0] * m[2][1] * m[0][3] - m[2][0] * m[3][1] * m[0][3] - m[3][0] * m[0][1] * m[2][3] + m[0][0] * m[3][1] * m[2][3] + m[2][0] * m[0][1] * m[3][3] - m[0][0] * m[2][1] * m[3][3], "
+		          "m[1][0] * m[3][1] * m[0][3] - m[3][0] * m[1][1] * m[0][3] + m[3][0] * m[0][1] * m[1][3] - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] + m[0][0] * m[1][1] * m[3][3], "
+		          "m[2][0] * m[1][1] * m[0][3] - m[1][0] * m[2][1] * m[0][3] - m[2][0] * m[0][1] * m[1][3] + m[0][0] * m[2][1] * m[1][3] + m[1][0] * m[0][1] * m[2][3] - m[0][0] * m[1][1] * m[2][3], "
+		          "t[3], "
+		          "m[2][0] * m[3][1] * m[0][2] - m[3][0] * m[2][1] * m[0][2] + m[3][0] * m[0][1] * m[2][2] - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] + m[0][0] * m[2][1] * m[3][2], "
+		          "m[3][0] * m[1][1] * m[0][2] - m[1][0] * m[3][1] * m[0][2] - m[3][0] * m[0][1] * m[1][2] + m[0][0] * m[3][1] * m[1][2] + m[1][0] * m[0][1] * m[3][2] - m[0][0] * m[1][1] * m[3][2], "
+		          "m[1][0] * m[2][1] * m[0][2] - m[2][0] * m[1][1] * m[0][2] + m[2][0] * m[0][1] * m[1][2] - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) "
+		          "* (1.0 / dot(m[0], t));");
+		end_scope();
+		statement("");
+	}
 }
 }
 
 
 // Returns a string representation of the ID, usable as a function arg.
 // Returns a string representation of the ID, usable as a function arg.
@@ -4849,8 +4981,9 @@ string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read)
 			// when consuming an access chain expression.
 			// when consuming an access chain expression.
 			uint32_t physical_type_id = get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID);
 			uint32_t physical_type_id = get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID);
 			bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked);
 			bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked);
+			bool relaxed = has_decoration(id, DecorationRelaxedPrecision);
 			return convert_row_major_matrix(e.expression, get<SPIRType>(e.expression_type), physical_type_id,
 			return convert_row_major_matrix(e.expression, get<SPIRType>(e.expression_type), physical_type_id,
-			                                is_packed);
+			                                is_packed, relaxed);
 		}
 		}
 		else if (flattened_structs.count(id))
 		else if (flattened_structs.count(id))
 		{
 		{
@@ -6691,6 +6824,9 @@ string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtyp
 			require_extension_internal("GL_EXT_shadow_samplers");
 			require_extension_internal("GL_EXT_shadow_samplers");
 		else
 		else
 			SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES"));
 			SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES"));
+
+		if (imgtype.image.dim == spv::DimCube)
+			return "shadowCubeNV";
 	}
 	}
 
 
 	if (op == "textureSize")
 	if (op == "textureSize")
@@ -7845,8 +7981,22 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		break;
 		break;
 
 
 	case GLSLstd450Trunc:
 	case GLSLstd450Trunc:
-		emit_unary_func_op(result_type, id, args[0], "trunc");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "trunc");
+		else
+		{
+			// Implement by value-casting to int and back.
+			bool forward = should_forward(args[0]);
+			auto op0 = to_unpacked_expression(args[0]);
+			auto &op0_type = expression_type(args[0]);
+			auto via_type = op0_type;
+			via_type.basetype = SPIRType::Int;
+			auto expr = join(type_to_glsl(op0_type), "(", type_to_glsl(via_type), "(", op0, "))");
+			emit_op(result_type, id, expr, forward);
+			inherit_expression_dependencies(id, args[0]);
+		}
 		break;
 		break;
+
 	case GLSLstd450SAbs:
 	case GLSLstd450SAbs:
 		emit_unary_func_op_cast(result_type, id, args[0], "abs", int_type, int_type);
 		emit_unary_func_op_cast(result_type, id, args[0], "abs", int_type, int_type);
 		break;
 		break;
@@ -7888,18 +8038,47 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		else
 		else
 			emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma");
 			emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma");
 		break;
 		break;
+
 	case GLSLstd450Modf:
 	case GLSLstd450Modf:
 		register_call_out_argument(args[1]);
 		register_call_out_argument(args[1]);
-		forced_temporaries.insert(id);
-		emit_binary_func_op(result_type, id, args[0], args[1], "modf");
+		if (!is_legacy())
+		{
+			forced_temporaries.insert(id);
+			emit_binary_func_op(result_type, id, args[0], args[1], "modf");
+		}
+		else
+		{
+			//NB. legacy GLSL doesn't have trunc() either, so we do a value cast
+			auto &op1_type = expression_type(args[1]);
+			auto via_type = op1_type;
+			via_type.basetype = SPIRType::Int;
+			statement(to_expression(args[1]), " = ",
+			          type_to_glsl(op1_type), "(", type_to_glsl(via_type),
+			          "(", to_expression(args[0]), "));");
+			emit_binary_op(result_type, id, args[0], args[1], "-");
+		}
 		break;
 		break;
 
 
 	case GLSLstd450ModfStruct:
 	case GLSLstd450ModfStruct:
 	{
 	{
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
 		emit_uninitialized_temporary_expression(result_type, id);
 		emit_uninitialized_temporary_expression(result_type, id);
-		statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ",
-		          to_expression(id), ".", to_member_name(type, 1), ");");
+		if (!is_legacy())
+		{
+			statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ",
+			          to_expression(id), ".", to_member_name(type, 1), ");");
+		}
+		else
+		{
+			//NB. legacy GLSL doesn't have trunc() either, so we do a value cast
+			auto &op0_type = expression_type(args[0]);
+			auto via_type = op0_type;
+			via_type.basetype = SPIRType::Int;
+			statement(to_expression(id), ".", to_member_name(type, 1), " = ", type_to_glsl(op0_type),
+			          "(", type_to_glsl(via_type), "(", to_expression(args[0]), "));");
+			statement(to_expression(id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(args[0]), " - ",
+			          to_expression(id), ".", to_member_name(type, 1), ";");
+		}
 		break;
 		break;
 	}
 	}
 
 
@@ -7960,22 +8139,77 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		emit_unary_func_op(result_type, id, args[0], "atan");
 		emit_unary_func_op(result_type, id, args[0], "atan");
 		break;
 		break;
 	case GLSLstd450Sinh:
 	case GLSLstd450Sinh:
-		emit_unary_func_op(result_type, id, args[0], "sinh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "sinh");
+		else
+		{
+			bool forward = should_forward(args[0]);
+			auto expr = join("(exp(", to_expression(args[0]), ") - exp(-", to_enclosed_expression(args[0]), ")) * 0.5");
+			emit_op(result_type, id, expr, forward);
+			inherit_expression_dependencies(id, args[0]);
+		}
 		break;
 		break;
 	case GLSLstd450Cosh:
 	case GLSLstd450Cosh:
-		emit_unary_func_op(result_type, id, args[0], "cosh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "cosh");
+		else
+		{
+			bool forward = should_forward(args[0]);
+			auto expr = join("(exp(", to_expression(args[0]), ") + exp(-", to_enclosed_expression(args[0]), ")) * 0.5");
+			emit_op(result_type, id, expr, forward);
+			inherit_expression_dependencies(id, args[0]);
+		}
 		break;
 		break;
 	case GLSLstd450Tanh:
 	case GLSLstd450Tanh:
-		emit_unary_func_op(result_type, id, args[0], "tanh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "tanh");
+		else
+		{
+			// Create temporaries to store the result of exp(arg) and exp(-arg).
+			uint32_t &ids = extra_sub_expressions[id];
+			if (!ids)
+			{
+				ids = ir.increase_bound_by(2);
+
+				// Inherit precision qualifier (legacy has no NoContraction).
+				if (has_decoration(id, DecorationRelaxedPrecision))
+				{
+					set_decoration(ids, DecorationRelaxedPrecision);
+					set_decoration(ids + 1, DecorationRelaxedPrecision);
+				}
+			}
+			uint32_t epos_id = ids;
+			uint32_t eneg_id = ids + 1;
+
+			emit_op(result_type, epos_id, join("exp(", to_expression(args[0]), ")"), false);
+			emit_op(result_type, eneg_id, join("exp(-", to_enclosed_expression(args[0]), ")"), false);
+			inherit_expression_dependencies(epos_id, args[0]);
+			inherit_expression_dependencies(eneg_id, args[0]);
+
+			auto expr = join("(", to_enclosed_expression(epos_id), " - ", to_enclosed_expression(eneg_id), ") / "
+			                 "(", to_enclosed_expression(epos_id), " + ", to_enclosed_expression(eneg_id), ")");
+			emit_op(result_type, id, expr, true);
+			inherit_expression_dependencies(id, epos_id);
+			inherit_expression_dependencies(id, eneg_id);
+		}
 		break;
 		break;
 	case GLSLstd450Asinh:
 	case GLSLstd450Asinh:
-		emit_unary_func_op(result_type, id, args[0], "asinh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "asinh");
+		else
+			emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Asinh);
 		break;
 		break;
 	case GLSLstd450Acosh:
 	case GLSLstd450Acosh:
-		emit_unary_func_op(result_type, id, args[0], "acosh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "acosh");
+		else
+			emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Acosh);
 		break;
 		break;
 	case GLSLstd450Atanh:
 	case GLSLstd450Atanh:
-		emit_unary_func_op(result_type, id, args[0], "atanh");
+		if (!is_legacy())
+			emit_unary_func_op(result_type, id, args[0], "atanh");
+		else
+			emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Atanh);
 		break;
 		break;
 	case GLSLstd450Atan2:
 	case GLSLstd450Atan2:
 		emit_binary_func_op(result_type, id, args[0], args[1], "atan");
 		emit_binary_func_op(result_type, id, args[0], args[1], "atan");
@@ -8006,11 +8240,74 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 
 
 	// Matrix math
 	// Matrix math
 	case GLSLstd450Determinant:
 	case GLSLstd450Determinant:
-		emit_unary_func_op(result_type, id, args[0], "determinant");
+	{
+		// No need to transpose - it doesn't affect the determinant
+		auto *e = maybe_get<SPIRExpression>(args[0]);
+		bool old_transpose = e && e->need_transpose;
+		if (old_transpose)
+			e->need_transpose = false;
+
+		if (options.version < 150) // also matches ES 100
+		{
+			auto &type = expression_type(args[0]);
+			assert(type.vecsize >= 2 && type.vecsize <= 4);
+			assert(type.vecsize == type.columns);
+
+			// ARB_gpu_shader_fp64 needs GLSL 150, other types are not valid
+			if (type.basetype != SPIRType::Float)
+				SPIRV_CROSS_THROW("Unsupported type for matrix determinant");
+
+			bool relaxed = has_decoration(id, DecorationRelaxedPrecision);
+			require_polyfill(static_cast<Polyfill>(PolyfillDeterminant2x2 << (type.vecsize - 2)),
+			                 relaxed);
+			emit_unary_func_op(result_type, id, args[0],
+			                   (options.es && relaxed) ? "spvDeterminantMP" : "spvDeterminant");
+		}
+		else
+			emit_unary_func_op(result_type, id, args[0], "determinant");
+
+		if (old_transpose)
+			e->need_transpose = true;
 		break;
 		break;
+	}
+
 	case GLSLstd450MatrixInverse:
 	case GLSLstd450MatrixInverse:
-		emit_unary_func_op(result_type, id, args[0], "inverse");
+	{
+		// The inverse of the transpose is the same as the transpose of
+		// the inverse, so we can just flip need_transpose of the result.
+		auto *a = maybe_get<SPIRExpression>(args[0]);
+		bool old_transpose = a && a->need_transpose;
+		if (old_transpose)
+			a->need_transpose = false;
+
+		const char *func = "inverse";
+		if (options.version < 140) // also matches ES 100
+		{
+			auto &type = get<SPIRType>(result_type);
+			assert(type.vecsize >= 2 && type.vecsize <= 4);
+			assert(type.vecsize == type.columns);
+
+			// ARB_gpu_shader_fp64 needs GLSL 150, other types are invalid
+			if (type.basetype != SPIRType::Float)
+				SPIRV_CROSS_THROW("Unsupported type for matrix inverse");
+
+			bool relaxed = has_decoration(id, DecorationRelaxedPrecision);
+			require_polyfill(static_cast<Polyfill>(PolyfillMatrixInverse2x2 << (type.vecsize - 2)),
+			                 relaxed);
+			func = (options.es && relaxed) ? "spvInverseMP" : "spvInverse";
+		}
+
+		bool forward = should_forward(args[0]);
+		auto &e = emit_op(result_type, id, join(func, "(", to_unpacked_expression(args[0]), ")"), forward);
+		inherit_expression_dependencies(id, args[0]);
+
+		if (old_transpose)
+		{
+			e.need_transpose = true;
+			a->need_transpose = true;
+		}
 		break;
 		break;
+	}
 
 
 	// Lerping
 	// Lerping
 	case GLSLstd450FMix:
 	case GLSLstd450FMix:
@@ -8203,13 +8500,60 @@ void CompilerGLSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t o
 	ir.meta[tmp_id] = ir.meta[id];
 	ir.meta[tmp_id] = ir.meta[id];
 	ir.meta[mixed_first_id] = ir.meta[id];
 	ir.meta[mixed_first_id] = ir.meta[id];
 
 
-	emit_unary_func_op(btype_id, left_nan_id, op0, "isnan");
-	emit_unary_func_op(btype_id, right_nan_id, op1, "isnan");
+	if (!is_legacy())
+	{
+		emit_unary_func_op(btype_id, left_nan_id, op0, "isnan");
+		emit_unary_func_op(btype_id, right_nan_id, op1, "isnan");
+	}
+	else if (expression_type(op0).vecsize > 1)
+	{
+		// If the number doesn't equal itself, it must be NaN
+		emit_binary_func_op(btype_id, left_nan_id, op0, op0, "notEqual");
+		emit_binary_func_op(btype_id, right_nan_id, op1, op1, "notEqual");
+	}
+	else
+	{
+		emit_binary_op(btype_id, left_nan_id, op0, op0, "!=");
+		emit_binary_op(btype_id, right_nan_id, op1, op1, "!=");
+	}
 	emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max");
 	emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max");
 	emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id);
 	emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id);
 	emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id);
 	emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id);
 }
 }
 
 
+void CompilerGLSL::emit_emulated_ahyper_op(uint32_t result_type, uint32_t id, uint32_t op0, GLSLstd450 op)
+{
+	const char *one = backend.float_literal_suffix ? "1.0f" : "1.0";
+	std::string expr;
+	bool forward = should_forward(op0);
+
+	switch (op)
+	{
+	case GLSLstd450Asinh:
+		expr = join("log(", to_enclosed_expression(op0), " + sqrt(",
+		            to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " + ", one, "))");
+		emit_op(result_type, id, expr, forward);
+		break;
+
+	case GLSLstd450Acosh:
+		expr = join("log(", to_enclosed_expression(op0), " + sqrt(",
+		            to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " - ", one, "))");
+		break;
+
+	case GLSLstd450Atanh:
+		expr = join("log((", one, " + ", to_enclosed_expression(op0), ") / "
+		            "(", one, " - ", to_enclosed_expression(op0), ")) * 0.5",
+		            backend.float_literal_suffix ? "f" : "");
+		break;
+
+	default:
+		SPIRV_CROSS_THROW("Invalid op.");
+	}
+
+	emit_op(result_type, id, expr, forward);
+	inherit_expression_dependencies(id, op0);
+}
+
 void CompilerGLSL::emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args,
 void CompilerGLSL::emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args,
                                                  uint32_t)
                                                  uint32_t)
 {
 {
@@ -8793,9 +9137,17 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
 	case BuiltInPointSize:
 	case BuiltInPointSize:
 		return "gl_PointSize";
 		return "gl_PointSize";
 	case BuiltInClipDistance:
 	case BuiltInClipDistance:
+	{
+		if (options.es)
+			require_extension_internal("GL_EXT_clip_cull_distance");
 		return "gl_ClipDistance";
 		return "gl_ClipDistance";
+	}
 	case BuiltInCullDistance:
 	case BuiltInCullDistance:
+	{
+		if (options.es)
+			require_extension_internal("GL_EXT_clip_cull_distance");
 		return "gl_CullDistance";
 		return "gl_CullDistance";
+	}
 	case BuiltInVertexId:
 	case BuiltInVertexId:
 		if (options.vulkan_semantics)
 		if (options.vulkan_semantics)
 			SPIRV_CROSS_THROW("Cannot implement gl_VertexID in Vulkan GLSL. This shader was created "
 			SPIRV_CROSS_THROW("Cannot implement gl_VertexID in Vulkan GLSL. This shader was created "
@@ -8940,17 +9292,21 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
 		return "gl_DrawIDARB";
 		return "gl_DrawIDARB";
 
 
 	case BuiltInSampleId:
 	case BuiltInSampleId:
-		if (options.es && options.version < 320)
+		if (is_legacy())
+			SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL.");
+		else if (options.es && options.version < 320)
 			require_extension_internal("GL_OES_sample_variables");
 			require_extension_internal("GL_OES_sample_variables");
-		if (!options.es && options.version < 400)
-			SPIRV_CROSS_THROW("gl_SampleID not supported before GLSL 400.");
+		else if (!options.es && options.version < 400)
+			require_extension_internal("GL_ARB_sample_shading");
 		return "gl_SampleID";
 		return "gl_SampleID";
 
 
 	case BuiltInSampleMask:
 	case BuiltInSampleMask:
-		if (options.es && options.version < 320)
+		if (is_legacy())
+			SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL.");
+		else if (options.es && options.version < 320)
 			require_extension_internal("GL_OES_sample_variables");
 			require_extension_internal("GL_OES_sample_variables");
-		if (!options.es && options.version < 400)
-			SPIRV_CROSS_THROW("gl_SampleMask/gl_SampleMaskIn not supported before GLSL 400.");
+		else if (!options.es && options.version < 400)
+			require_extension_internal("GL_ARB_sample_shading");
 
 
 		if (storage == StorageClassInput)
 		if (storage == StorageClassInput)
 			return "gl_SampleMaskIn";
 			return "gl_SampleMaskIn";
@@ -8958,10 +9314,12 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
 			return "gl_SampleMask";
 			return "gl_SampleMask";
 
 
 	case BuiltInSamplePosition:
 	case BuiltInSamplePosition:
-		if (options.es && options.version < 320)
+		if (is_legacy())
+			SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL.");
+		else if (options.es && options.version < 320)
 			require_extension_internal("GL_OES_sample_variables");
 			require_extension_internal("GL_OES_sample_variables");
-		if (!options.es && options.version < 400)
-			SPIRV_CROSS_THROW("gl_SamplePosition not supported before GLSL 400.");
+		else if (!options.es && options.version < 400)
+			require_extension_internal("GL_ARB_sample_shading");
 		return "gl_SamplePosition";
 		return "gl_SamplePosition";
 
 
 	case BuiltInViewIndex:
 	case BuiltInViewIndex:
@@ -9777,10 +10135,13 @@ std::string CompilerGLSL::flattened_access_chain_struct(uint32_t base, const uin
 		// The access chain terminates at the struct, so we need to find matrix strides and row-major information
 		// The access chain terminates at the struct, so we need to find matrix strides and row-major information
 		// ahead of time.
 		// ahead of time.
 		bool need_transpose = false;
 		bool need_transpose = false;
+		bool relaxed = false;
 		uint32_t matrix_stride = 0;
 		uint32_t matrix_stride = 0;
 		if (member_type.columns > 1)
 		if (member_type.columns > 1)
 		{
 		{
-			need_transpose = combined_decoration_for_member(target_type, i).get(DecorationRowMajor);
+			auto decorations = combined_decoration_for_member(target_type, i);
+			need_transpose = decorations.get(DecorationRowMajor);
+			relaxed = decorations.get(DecorationRelaxedPrecision);
 			matrix_stride = type_struct_member_matrix_stride(target_type, i);
 			matrix_stride = type_struct_member_matrix_stride(target_type, i);
 		}
 		}
 
 
@@ -9789,7 +10150,7 @@ std::string CompilerGLSL::flattened_access_chain_struct(uint32_t base, const uin
 
 
 		// Cannot forward transpositions, so resolve them here.
 		// Cannot forward transpositions, so resolve them here.
 		if (need_transpose)
 		if (need_transpose)
-			expr += convert_row_major_matrix(tmp, member_type, 0, false);
+			expr += convert_row_major_matrix(tmp, member_type, 0, false, relaxed);
 		else
 		else
 			expr += tmp;
 			expr += tmp;
 	}
 	}
@@ -11809,11 +12170,57 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 
 
 	// ALU
 	// ALU
 	case OpIsNan:
 	case OpIsNan:
-		GLSL_UFOP(isnan);
+		if (!is_legacy())
+			GLSL_UFOP(isnan);
+		else
+		{
+			// Check if the number doesn't equal itself
+			auto &type = get<SPIRType>(ops[0]);
+			if (type.vecsize > 1)
+				emit_binary_func_op(ops[0], ops[1], ops[2], ops[2], "notEqual");
+			else
+				emit_binary_op(ops[0], ops[1], ops[2], ops[2], "!=");
+		}
 		break;
 		break;
 
 
 	case OpIsInf:
 	case OpIsInf:
-		GLSL_UFOP(isinf);
+		if (!is_legacy())
+			GLSL_UFOP(isinf);
+		else
+		{
+			// inf * 2 == inf by IEEE 754 rules, note this also applies to 0.0
+			// This is more reliable than checking if product with zero is NaN
+			uint32_t result_type = ops[0];
+			uint32_t result_id = ops[1];
+			uint32_t operand = ops[2];
+
+			auto &type = get<SPIRType>(result_type);
+			std::string expr;
+			if (type.vecsize > 1)
+			{
+				expr = type_to_glsl_constructor(type);
+				expr += '(';
+				for (uint32_t i = 0; i < type.vecsize; i++)
+				{
+					auto comp = to_extract_component_expression(operand, i);
+					expr += join(comp, " != 0.0 && 2.0 * ", comp, " == ", comp);
+
+					if (i + 1 < type.vecsize)
+						expr += ", ";
+				}
+				expr += ')';
+			}
+			else
+			{
+				// Register an extra read to force writing out a temporary
+				auto oper = to_enclosed_expression(operand);
+				track_expression_read(operand);
+				expr += join(oper, " != 0.0 && 2.0 * ", oper, " == ", oper);
+			}
+			emit_op(result_type, result_id, expr, should_forward(operand));
+
+			inherit_expression_dependencies(result_id, operand);
+		}
 		break;
 		break;
 
 
 	case OpSNegate:
 	case OpSNegate:
@@ -11912,14 +12319,59 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		break;
 		break;
 	}
 	}
 
 
-	case OpFMul:
 	case OpMatrixTimesScalar:
 	case OpMatrixTimesScalar:
+	{
+		auto *a = maybe_get<SPIRExpression>(ops[2]);
+
+		// If the matrix need transpose, just mark the result as needing so.
+		if (a && a->need_transpose)
+		{
+			a->need_transpose = false;
+			auto expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[2])), " * ",
+			                 to_enclosed_unpacked_expression(ops[3]));
+			bool forward = should_forward(ops[2]) && should_forward(ops[3]);
+			auto &e = emit_op(ops[0], ops[1], expr, forward);
+			e.need_transpose = true;
+			a->need_transpose = true;
+			inherit_expression_dependencies(ops[1], ops[2]);
+			inherit_expression_dependencies(ops[1], ops[3]);
+		}
+		else
+			GLSL_BOP(*);
+		break;
+	}
+
+	case OpFMul:
 	case OpVectorTimesScalar:
 	case OpVectorTimesScalar:
 		GLSL_BOP(*);
 		GLSL_BOP(*);
 		break;
 		break;
 
 
 	case OpOuterProduct:
 	case OpOuterProduct:
-		GLSL_BFOP(outerProduct);
+		if (options.version < 120) // Matches GLSL 1.10 / ESSL 1.00
+		{
+			uint32_t result_type = ops[0];
+			uint32_t id = ops[1];
+			uint32_t a = ops[2];
+			uint32_t b = ops[3];
+
+			auto &type = get<SPIRType>(result_type);
+			string expr = type_to_glsl_constructor(type);
+			expr += "(";
+			for (uint32_t col = 0; col < type.columns; col++)
+			{
+				expr += to_enclosed_expression(a);
+				expr += " * ";
+				expr += to_extract_component_expression(b, col);
+				if (col + 1 < type.columns)
+					expr += ", ";
+			}
+			expr += ")";
+			emit_op(result_type, id, expr, should_forward(a) && should_forward(b));
+			inherit_expression_dependencies(id, a);
+			inherit_expression_dependencies(id, b);
+		}
+		else
+			GLSL_BFOP(outerProduct);
 		break;
 		break;
 
 
 	case OpDot:
 	case OpDot:
@@ -12087,10 +12539,6 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 
 
 	case OpFRem:
 	case OpFRem:
 	{
 	{
-		if (is_legacy())
-			SPIRV_CROSS_THROW("OpFRem requires trunc() and is only supported on non-legacy targets. A workaround is "
-			                  "needed for legacy.");
-
 		uint32_t result_type = ops[0];
 		uint32_t result_type = ops[0];
 		uint32_t result_id = ops[1];
 		uint32_t result_id = ops[1];
 		uint32_t op0 = ops[2];
 		uint32_t op0 = ops[2];
@@ -12098,8 +12546,22 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 
 
 		// Needs special handling.
 		// Needs special handling.
 		bool forward = should_forward(op0) && should_forward(op1);
 		bool forward = should_forward(op0) && should_forward(op1);
-		auto expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "trunc(",
-		                 to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")");
+		std::string expr;
+		if (!is_legacy())
+		{
+			expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "trunc(",
+			            to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")");
+		}
+		else
+		{
+			// Legacy GLSL has no trunc, emulate by casting to int and back
+			auto &op0_type = expression_type(op0);
+			auto via_type = op0_type;
+			via_type.basetype = SPIRType::Int;
+			expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ",
+			            type_to_glsl(op0_type), "(", type_to_glsl(via_type),  "(",
+			            to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), "))");
+		}
 
 
 		emit_op(result_type, result_id, expr, forward);
 		emit_op(result_type, result_id, expr, forward);
 		inherit_expression_dependencies(result_id, op0);
 		inherit_expression_dependencies(result_id, op0);
@@ -12753,7 +13215,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 			op = "textureQueryLOD";
 			op = "textureQueryLOD";
 		}
 		}
 		else if (options.es)
 		else if (options.es)
-			SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile.");
+		{
+			if (options.version < 300)
+				SPIRV_CROSS_THROW("textureQueryLod not supported in legacy ES");
+			require_extension_internal("GL_EXT_texture_query_lod");
+			op = "textureQueryLOD";
+		}
 		else
 		else
 			op = "textureQueryLod";
 			op = "textureQueryLod";
 
 
@@ -12799,6 +13266,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		uint32_t result_type = ops[0];
 		uint32_t result_type = ops[0];
 		uint32_t id = ops[1];
 		uint32_t id = ops[1];
 
 
+		if (options.es)
+			SPIRV_CROSS_THROW("textureSamples and imageSamples not supported in ES profile.");
+		else if (options.version < 450)
+			require_extension_internal("GL_ARB_texture_query_samples");
+
 		string expr;
 		string expr;
 		if (type.image.sampled == 2)
 		if (type.image.sampled == 2)
 			expr = join("imageSamples(", to_non_uniform_aware_expression(ops[2]), ")");
 			expr = join("imageSamples(", to_non_uniform_aware_expression(ops[2]), ")");
@@ -13841,6 +14313,42 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		statement("SetMeshOutputsEXT(", to_unpacked_expression(ops[0]), ", ", to_unpacked_expression(ops[1]), ");");
 		statement("SetMeshOutputsEXT(", to_unpacked_expression(ops[0]), ", ", to_unpacked_expression(ops[1]), ");");
 		break;
 		break;
 
 
+	case OpReadClockKHR:
+	{
+		auto &type = get<SPIRType>(ops[0]);
+		auto scope = static_cast<Scope>(evaluate_constant_u32(ops[2]));
+		const char *op = nullptr;
+		// Forwarding clock statements leads to a scenario where an SSA value can take on different
+		// values every time it's evaluated. Block any forwarding attempt.
+		// We also might want to invalidate all expressions to function as a sort of optimization
+		// barrier, but might be overkill for now.
+		if (scope == ScopeDevice)
+		{
+			require_extension_internal("GL_EXT_shader_realtime_clock");
+			if (type.basetype == SPIRType::BaseType::UInt64)
+				op = "clockRealtimeEXT()";
+			else if (type.basetype == SPIRType::BaseType::UInt && type.vecsize == 2)
+				op = "clockRealtime2x32EXT()";
+			else
+				SPIRV_CROSS_THROW("Unsupported result type for OpReadClockKHR opcode.");
+		}
+		else if (scope == ScopeSubgroup)
+		{
+			require_extension_internal("GL_ARB_shader_clock");
+			if (type.basetype == SPIRType::BaseType::UInt64)
+				op = "clockARB()";
+			else if (type.basetype == SPIRType::BaseType::UInt && type.vecsize == 2)
+				op = "clock2x32ARB()";
+			else
+				SPIRV_CROSS_THROW("Unsupported result type for OpReadClockKHR opcode.");
+		}
+		else
+			SPIRV_CROSS_THROW("Unsupported scope for OpReadClockKHR opcode.");
+
+		emit_op(ops[0], ops[1], op, false);
+		break;
+	}
+
 	default:
 	default:
 		statement("// unimplemented op ", instruction.op);
 		statement("// unimplemented op ", instruction.op);
 		break;
 		break;
@@ -13973,7 +14481,7 @@ bool CompilerGLSL::member_is_packed_physical_type(const SPIRType &type, uint32_t
 // Base implementation uses the standard library transpose() function.
 // Base implementation uses the standard library transpose() function.
 // Subclasses may override to use a different function.
 // Subclasses may override to use a different function.
 string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t /* physical_type_id */,
 string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t /* physical_type_id */,
-                                              bool /*is_packed*/)
+                                              bool /*is_packed*/, bool relaxed)
 {
 {
 	strip_enclosed_expression(exp_str);
 	strip_enclosed_expression(exp_str);
 	if (!is_matrix(exp_type))
 	if (!is_matrix(exp_type))
@@ -14003,32 +14511,14 @@ string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &ex
 		// GLSL 110, ES 100 do not have transpose(), so emulate it.  Note that
 		// GLSL 110, ES 100 do not have transpose(), so emulate it.  Note that
 		// these GLSL versions do not support non-square matrices.
 		// these GLSL versions do not support non-square matrices.
 		if (exp_type.vecsize == 2 && exp_type.columns == 2)
 		if (exp_type.vecsize == 2 && exp_type.columns == 2)
-		{
-			if (!requires_transpose_2x2)
-			{
-				requires_transpose_2x2 = true;
-				force_recompile();
-			}
-		}
+			require_polyfill(PolyfillTranspose2x2, relaxed);
 		else if (exp_type.vecsize == 3 && exp_type.columns == 3)
 		else if (exp_type.vecsize == 3 && exp_type.columns == 3)
-		{
-			if (!requires_transpose_3x3)
-			{
-				requires_transpose_3x3 = true;
-				force_recompile();
-			}
-		}
+			require_polyfill(PolyfillTranspose3x3, relaxed);
 		else if (exp_type.vecsize == 4 && exp_type.columns == 4)
 		else if (exp_type.vecsize == 4 && exp_type.columns == 4)
-		{
-			if (!requires_transpose_4x4)
-			{
-				requires_transpose_4x4 = true;
-				force_recompile();
-			}
-		}
+			require_polyfill(PolyfillTranspose4x4, relaxed);
 		else
 		else
 			SPIRV_CROSS_THROW("Non-square matrices are not supported in legacy GLSL, cannot transpose.");
 			SPIRV_CROSS_THROW("Non-square matrices are not supported in legacy GLSL, cannot transpose.");
-		return join("spvTranspose(", exp_str, ")");
+		return join("spvTranspose", (options.es && relaxed) ? "MP" : "", "(", exp_str, ")");
 	}
 	}
 	else
 	else
 		return join("transpose(", exp_str, ")");
 		return join("transpose(", exp_str, ")");
@@ -14531,6 +15021,17 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id)
 	    is_depth_image(type, id))
 	    is_depth_image(type, id))
 	{
 	{
 		res += "Shadow";
 		res += "Shadow";
+
+		if (type.image.dim == DimCube && is_legacy())
+		{
+			if (!options.es)
+				require_extension_internal("GL_EXT_gpu_shader4");
+			else
+			{
+				require_extension_internal("GL_NV_shadow_samplers_cube");
+				res += "NV";
+			}
+		}
 	}
 	}
 
 
 	return res;
 	return res;
@@ -14610,7 +15111,20 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id)
 	}
 	}
 
 
 	if (type.basetype == SPIRType::UInt && is_legacy())
 	if (type.basetype == SPIRType::UInt && is_legacy())
-		SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets.");
+	{
+		if (options.es)
+			SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy ESSL.");
+		else
+			require_extension_internal("GL_EXT_gpu_shader4");
+	}
+
+	if (type.basetype == SPIRType::AtomicCounter)
+	{
+		if (options.es && options.version < 310)
+			SPIRV_CROSS_THROW("At least ESSL 3.10 required for atomic counters.");
+		else if (!options.es && options.version < 420)
+			require_extension_internal("GL_ARB_shader_atomic_counters");
+	}
 
 
 	if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
 	if (type.vecsize == 1 && type.columns == 1) // Scalar builtin
 	{
 	{
@@ -16654,7 +17168,12 @@ void CompilerGLSL::cast_from_variable_load(uint32_t source_id, std::string &expr
 
 
 	// Only interested in standalone builtin variables.
 	// Only interested in standalone builtin variables.
 	if (!has_decoration(source_id, DecorationBuiltIn))
 	if (!has_decoration(source_id, DecorationBuiltIn))
+	{
+		// Except for int attributes in legacy GLSL, which are cast from float.
+		if (is_legacy() && expr_type.basetype == SPIRType::Int && var && var->storage == StorageClassInput)
+			expr = join(type_to_glsl(expr_type), "(", expr, ")");
 		return;
 		return;
+	}
 
 
 	auto builtin = static_cast<BuiltIn>(get_decoration(source_id, DecorationBuiltIn));
 	auto builtin = static_cast<BuiltIn>(get_decoration(source_id, DecorationBuiltIn));
 	auto expected_type = expr_type.basetype;
 	auto expected_type = expr_type.basetype;

+ 21 - 4
3rdparty/spirv-cross/spirv_glsl.hpp

@@ -552,7 +552,8 @@ protected:
 	bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const;
 	bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const;
 	bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const;
 	bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const;
 	virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type,
 	virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type,
-	                                             uint32_t physical_type_id, bool is_packed);
+	                                             uint32_t physical_type_id, bool is_packed,
+	                                             bool relaxed = false);
 
 
 	std::unordered_set<std::string> local_variable_names;
 	std::unordered_set<std::string> local_variable_names;
 	std::unordered_set<std::string> resource_names;
 	std::unordered_set<std::string> resource_names;
@@ -626,6 +627,7 @@ protected:
 	void emit_struct(SPIRType &type);
 	void emit_struct(SPIRType &type);
 	void emit_resources();
 	void emit_resources();
 	void emit_extension_workarounds(spv::ExecutionModel model);
 	void emit_extension_workarounds(spv::ExecutionModel model);
+	void emit_polyfills(uint32_t polyfills, bool relaxed);
 	void emit_buffer_block_native(const SPIRVariable &var);
 	void emit_buffer_block_native(const SPIRVariable &var);
 	void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
 	void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
 	void emit_buffer_block_legacy(const SPIRVariable &var);
 	void emit_buffer_block_legacy(const SPIRVariable &var);
@@ -663,6 +665,7 @@ protected:
 	bool should_suppress_usage_tracking(uint32_t id) const;
 	bool should_suppress_usage_tracking(uint32_t id) const;
 	void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp);
 	void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp);
 	void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op);
 	void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op);
+	void emit_emulated_ahyper_op(uint32_t result_type, uint32_t result_id, uint32_t op0, GLSLstd450 op);
 	bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp);
 	bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp);
 	void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
 	void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
 	                             uint32_t op3, const char *op);
 	                             uint32_t op3, const char *op);
@@ -883,9 +886,23 @@ protected:
 		return !options.es && options.version < 130;
 		return !options.es && options.version < 130;
 	}
 	}
 
 
-	bool requires_transpose_2x2 = false;
-	bool requires_transpose_3x3 = false;
-	bool requires_transpose_4x4 = false;
+	enum Polyfill : uint32_t
+	{
+		PolyfillTranspose2x2 = 1 << 0,
+		PolyfillTranspose3x3 = 1 << 1,
+		PolyfillTranspose4x4 = 1 << 2,
+		PolyfillDeterminant2x2 = 1 << 3,
+		PolyfillDeterminant3x3 = 1 << 4,
+		PolyfillDeterminant4x4 = 1 << 5,
+		PolyfillMatrixInverse2x2 = 1 << 6,
+		PolyfillMatrixInverse3x3 = 1 << 7,
+		PolyfillMatrixInverse4x4 = 1 << 8,
+	};
+
+	uint32_t required_polyfills = 0;
+	uint32_t required_polyfills_relaxed = 0;
+	void require_polyfill(Polyfill polyfill, bool relaxed);
+
 	bool ray_tracing_is_khr = false;
 	bool ray_tracing_is_khr = false;
 	bool barycentric_is_nv = false;
 	bool barycentric_is_nv = false;
 	void ray_tracing_khr_fixup_locations();
 	void ray_tracing_khr_fixup_locations();

+ 24 - 9
3rdparty/spirv-cross/spirv_hlsl.cpp

@@ -682,10 +682,14 @@ void CompilerHLSL::emit_builtin_outputs_in_struct()
 			// If point_size_compat is enabled, just ignore PointSize.
 			// If point_size_compat is enabled, just ignore PointSize.
 			// PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders,
 			// PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders,
 			// even if it means working around the missing feature.
 			// even if it means working around the missing feature.
-			if (hlsl_options.point_size_compat)
-				break;
-			else
+			if (legacy)
+			{
+				type = "float";
+				semantic = "PSIZE";
+			}
+			else if (!hlsl_options.point_size_compat)
 				SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
 				SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
+			break;
 
 
 		case BuiltInLayer:
 		case BuiltInLayer:
 		case BuiltInPrimitiveId:
 		case BuiltInPrimitiveId:
@@ -1016,6 +1020,7 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 
 
 	string binding;
 	string binding;
 	bool use_location_number = true;
 	bool use_location_number = true;
+	bool need_matrix_unroll = false;
 	bool legacy = hlsl_options.shader_model <= 30;
 	bool legacy = hlsl_options.shader_model <= 30;
 	if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
 	if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput)
 	{
 	{
@@ -1031,6 +1036,12 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 		if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP)
 		if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP)
 			type.vecsize = 4;
 			type.vecsize = 4;
 	}
 	}
+	else if (var.storage == StorageClassInput && execution.model == ExecutionModelVertex)
+	{
+		need_matrix_unroll = true;
+		if (legacy) // Inputs must be floating-point in legacy targets.
+			type.basetype = SPIRType::Float;
+	}
 
 
 	const auto get_vacant_location = [&]() -> uint32_t {
 	const auto get_vacant_location = [&]() -> uint32_t {
 		for (uint32_t i = 0; i < 64; i++)
 		for (uint32_t i = 0; i < 64; i++)
@@ -1039,8 +1050,6 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 		SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted.");
 		SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted.");
 	};
 	};
 
 
-	bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
-
 	auto name = to_name(var.self);
 	auto name = to_name(var.self);
 	if (use_location_number)
 	if (use_location_number)
 	{
 	{
@@ -1221,7 +1230,7 @@ void CompilerHLSL::emit_builtin_variables()
 			break;
 			break;
 
 
 		case BuiltInPointSize:
 		case BuiltInPointSize:
-			if (hlsl_options.point_size_compat)
+			if (hlsl_options.point_size_compat || hlsl_options.shader_model <= 30)
 			{
 			{
 				// Just emit the global variable, it will be ignored.
 				// Just emit the global variable, it will be ignored.
 				type = "float";
 				type = "float";
@@ -3218,8 +3227,8 @@ void CompilerHLSL::emit_hlsl_entry_point()
 
 
 		// Copy builtins from globals to return struct.
 		// Copy builtins from globals to return struct.
 		active_output_builtins.for_each_bit([&](uint32_t i) {
 		active_output_builtins.for_each_bit([&](uint32_t i) {
-			// PointSize doesn't exist in HLSL.
-			if (i == BuiltInPointSize)
+			// PointSize doesn't exist in HLSL SM 4+.
+			if (i == BuiltInPointSize && !legacy)
 				return;
 				return;
 
 
 			switch (static_cast<BuiltIn>(i))
 			switch (static_cast<BuiltIn>(i))
@@ -4115,10 +4124,16 @@ void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		emit_unary_func_op(result_type, id, args[0], "round");
 		emit_unary_func_op(result_type, id, args[0], "round");
 		break;
 		break;
 
 
+	case GLSLstd450Trunc:
+		emit_unary_func_op(result_type, id, args[0], "trunc");
+		break;
+
 	case GLSLstd450Acosh:
 	case GLSLstd450Acosh:
 	case GLSLstd450Asinh:
 	case GLSLstd450Asinh:
 	case GLSLstd450Atanh:
 	case GLSLstd450Atanh:
-		SPIRV_CROSS_THROW("Inverse hyperbolics are not supported on HLSL.");
+		// These are not supported in HLSL, always emulate them.
+		emit_emulated_ahyper_op(result_type, id, args[0], op);
+		break;
 
 
 	case GLSLstd450FMix:
 	case GLSLstd450FMix:
 	case GLSLstd450IMix:
 	case GLSLstd450IMix:

+ 1 - 1
3rdparty/spirv-cross/spirv_hlsl.hpp

@@ -110,7 +110,7 @@ public:
 	{
 	{
 		uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
 		uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
 
 
-		// Allows the PointSize builtin, and ignores it, as PointSize is not supported in HLSL.
+		// Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+.
 		bool point_size_compat = false;
 		bool point_size_compat = false;
 
 
 		// Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.
 		// Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.

+ 11 - 11
3rdparty/spirv-cross/spirv_msl.cpp

@@ -2364,7 +2364,7 @@ bool CompilerMSL::add_component_variable_to_interface_block(spv::StorageClass st
 		if (pad_fragment_output)
 		if (pad_fragment_output)
 		{
 		{
 			uint32_t locn = get_decoration(var.self, DecorationLocation);
 			uint32_t locn = get_decoration(var.self, DecorationLocation);
-			num_components = std::max(num_components, get_target_components_for_fragment_location(locn));
+			num_components = max<uint32_t>(num_components, get_target_components_for_fragment_location(locn));
 		}
 		}
 
 
 		// We have already declared an IO block member as m_location_N.
 		// We have already declared an IO block member as m_location_N.
@@ -3846,7 +3846,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
 						for (uint32_t location_offset = 0; location_offset < array_size; location_offset++)
 						for (uint32_t location_offset = 0; location_offset < array_size; location_offset++)
 						{
 						{
 							auto &location_meta = meta.location_meta[location + location_offset];
 							auto &location_meta = meta.location_meta[location + location_offset];
-							location_meta.num_components = std::max(location_meta.num_components, component + type.vecsize);
+							location_meta.num_components = max<uint32_t>(location_meta.num_components, component + type.vecsize);
 
 
 							// For variables sharing location, decorations and base type must match.
 							// For variables sharing location, decorations and base type must match.
 							location_meta.base_type_id = type.self;
 							location_meta.base_type_id = type.self;
@@ -4515,7 +4515,7 @@ void CompilerMSL::mark_scalar_layout_structs(const SPIRType &type)
 				for (uint32_t dim = 0; dim < dimensions; dim++)
 				for (uint32_t dim = 0; dim < dimensions; dim++)
 				{
 				{
 					uint32_t array_size = to_array_size_literal(mbr_type, dim);
 					uint32_t array_size = to_array_size_literal(mbr_type, dim);
-					array_stride /= max(array_size, 1u);
+					array_stride /= max<uint32_t>(array_size, 1u);
 				}
 				}
 
 
 				// Set expected struct size based on ArrayStride.
 				// Set expected struct size based on ArrayStride.
@@ -4722,7 +4722,7 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in
 		// Hack off array-of-arrays until we find the array stride per element we must have to make it work.
 		// Hack off array-of-arrays until we find the array stride per element we must have to make it work.
 		uint32_t dimensions = uint32_t(mbr_type.array.size() - 1);
 		uint32_t dimensions = uint32_t(mbr_type.array.size() - 1);
 		for (uint32_t dim = 0; dim < dimensions; dim++)
 		for (uint32_t dim = 0; dim < dimensions; dim++)
-			array_stride /= max(to_array_size_literal(mbr_type, dim), 1u);
+			array_stride /= max<uint32_t>(to_array_size_literal(mbr_type, dim), 1u);
 
 
 		// Pointers are 8 bytes
 		// Pointers are 8 bytes
 		uint32_t mbr_width_in_bytes = is_buff_ptr ? 8 : (mbr_type.width / 8);
 		uint32_t mbr_width_in_bytes = is_buff_ptr ? 8 : (mbr_type.width / 8);
@@ -7206,7 +7206,7 @@ static string inject_top_level_storage_qualifier(const string &expr, const strin
 	else if (last_pointer == string::npos)
 	else if (last_pointer == string::npos)
 		last_significant = last_reference;
 		last_significant = last_reference;
 	else
 	else
-		last_significant = std::max(last_reference, last_pointer);
+		last_significant = max<size_t>(last_reference, last_pointer);
 
 
 	if (last_significant == string::npos)
 	if (last_significant == string::npos)
 		return join(qualifier, " ", expr);
 		return join(qualifier, " ", expr);
@@ -11487,11 +11487,11 @@ bool CompilerMSL::member_is_non_native_row_major_matrix(const SPIRType &type, ui
 }
 }
 
 
 string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
 string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
-                                             bool is_packed)
+                                             bool is_packed, bool relaxed)
 {
 {
 	if (!is_matrix(exp_type))
 	if (!is_matrix(exp_type))
 	{
 	{
-		return CompilerGLSL::convert_row_major_matrix(std::move(exp_str), exp_type, physical_type_id, is_packed);
+		return CompilerGLSL::convert_row_major_matrix(std::move(exp_str), exp_type, physical_type_id, is_packed, relaxed);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -15920,7 +15920,7 @@ uint32_t CompilerMSL::get_declared_type_array_stride_msl(const SPIRType &type, b
 	for (uint32_t dim = 0; dim < dimensions; dim++)
 	for (uint32_t dim = 0; dim < dimensions; dim++)
 	{
 	{
 		uint32_t array_size = to_array_size_literal(type, dim);
 		uint32_t array_size = to_array_size_literal(type, dim);
-		value_size *= max(array_size, 1u);
+		value_size *= max<uint32_t>(array_size, 1u);
 	}
 	}
 
 
 	return value_size;
 	return value_size;
@@ -16031,7 +16031,7 @@ uint32_t CompilerMSL::get_declared_type_size_msl(const SPIRType &type, bool is_p
 		if (!type.array.empty())
 		if (!type.array.empty())
 		{
 		{
 			uint32_t array_size = to_array_size_literal(type);
 			uint32_t array_size = to_array_size_literal(type);
-			return get_declared_type_array_stride_msl(type, is_packed, row_major) * max(array_size, 1u);
+			return get_declared_type_array_stride_msl(type, is_packed, row_major) * max<uint32_t>(array_size, 1u);
 		}
 		}
 
 
 		if (type.basetype == SPIRType::Struct)
 		if (type.basetype == SPIRType::Struct)
@@ -16705,7 +16705,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr,
 	bool is_packed = has_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypePacked);
 	bool is_packed = has_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypePacked);
 	auto *source_expr = maybe_get<SPIRExpression>(source_id);
 	auto *source_expr = maybe_get<SPIRExpression>(source_id);
 	auto *var = maybe_get_backing_variable(source_id);
 	auto *var = maybe_get_backing_variable(source_id);
-	const SPIRType *var_type, *phys_type;
+	const SPIRType *var_type = nullptr, *phys_type = nullptr;
 	if (uint32_t phys_id = get_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypeID))
 	if (uint32_t phys_id = get_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypeID))
 		phys_type = &get<SPIRType>(phys_id);
 		phys_type = &get<SPIRType>(phys_id);
 	else
 	else
@@ -16831,7 +16831,7 @@ void CompilerMSL::cast_to_variable_store(uint32_t target_id, std::string &expr,
 	bool is_packed = has_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypePacked);
 	bool is_packed = has_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypePacked);
 	auto *target_expr = maybe_get<SPIRExpression>(target_id);
 	auto *target_expr = maybe_get<SPIRExpression>(target_id);
 	auto *var = maybe_get_backing_variable(target_id);
 	auto *var = maybe_get_backing_variable(target_id);
-	const SPIRType *var_type, *phys_type;
+	const SPIRType *var_type = nullptr, *phys_type = nullptr;
 	if (uint32_t phys_id = get_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypeID))
 	if (uint32_t phys_id = get_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypeID))
 		phys_type = &get<SPIRType>(phys_id);
 		phys_type = &get<SPIRType>(phys_id);
 	else
 	else

+ 1 - 1
3rdparty/spirv-cross/spirv_msl.hpp

@@ -864,7 +864,7 @@ protected:
 	bool is_non_native_row_major_matrix(uint32_t id) override;
 	bool is_non_native_row_major_matrix(uint32_t id) override;
 	bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
 	bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
 	std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
 	std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
-	                                     bool is_packed) override;
+	                                     bool is_packed, bool relaxed) override;
 
 
 	bool is_tesc_shader() const;
 	bool is_tesc_shader() const;
 	bool is_tese_shader() const;
 	bool is_tese_shader() const;