浏览代码

Updated spirv-cross.

Бранимир Караџић 2 年之前
父节点
当前提交
6cedc01d19

+ 37 - 0
3rdparty/spirv-cross/spirv_cross_c.cpp

@@ -820,6 +820,43 @@ spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *
 #endif
 #endif
 }
 }
 
 
+size_t spvc_compiler_get_num_required_extensions(spvc_compiler compiler) 
+{
+#if SPIRV_CROSS_C_API_GLSL
+	if (compiler->backend != SPVC_BACKEND_GLSL)
+	{
+		compiler->context->report_error("Enabled extensions can only be queried on GLSL backend.");
+		return SPVC_ERROR_INVALID_ARGUMENT;
+	}
+
+	return static_cast<CompilerGLSL *>(compiler->compiler.get())->get_required_extensions().size();
+#else
+	compiler->context->report_error("Enabled extensions can only be queried on GLSL backend.");
+	return 0;
+#endif
+}
+
+const char *spvc_compiler_get_required_extension(spvc_compiler compiler, size_t index)
+{
+#if SPIRV_CROSS_C_API_GLSL
+	if (compiler->backend != SPVC_BACKEND_GLSL)
+	{
+		compiler->context->report_error("Enabled extensions can only be queried on GLSL backend.");
+		return nullptr;
+	}
+
+	auto &exts = static_cast<CompilerGLSL *>(compiler->compiler.get())->get_required_extensions();
+	if (index < exts.size())
+		return exts[index].c_str();
+	else
+		return nullptr;
+#else
+	(void)index;
+	compiler->context->report_error("Enabled extensions can only be queried on GLSL backend.");
+	return nullptr;
+#endif
+}
+
 spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id)
 spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id)
 {
 {
 #if SPIRV_CROSS_C_API_GLSL
 #if SPIRV_CROSS_C_API_GLSL

+ 3 - 1
3rdparty/spirv-cross/spirv_cross_c.h

@@ -40,7 +40,7 @@ extern "C" {
 /* Bumped if ABI or API breaks backwards compatibility. */
 /* Bumped if ABI or API breaks backwards compatibility. */
 #define SPVC_C_API_VERSION_MAJOR 0
 #define SPVC_C_API_VERSION_MAJOR 0
 /* Bumped if APIs or enumerations are added in a backwards compatible way. */
 /* Bumped if APIs or enumerations are added in a backwards compatible way. */
-#define SPVC_C_API_VERSION_MINOR 56
+#define SPVC_C_API_VERSION_MINOR 57
 /* Bumped if internal implementation details change. */
 /* Bumped if internal implementation details change. */
 #define SPVC_C_API_VERSION_PATCH 0
 #define SPVC_C_API_VERSION_PATCH 0
 
 
@@ -784,6 +784,8 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const
 /* Maps to C++ API. */
 /* Maps to C++ API. */
 SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line);
 SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line);
 SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext);
 SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext);
+SPVC_PUBLIC_API size_t spvc_compiler_get_num_required_extensions(spvc_compiler compiler);
+SPVC_PUBLIC_API const char *spvc_compiler_get_required_extension(spvc_compiler compiler, size_t index);
 SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id);
 SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id);
 
 
 SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id);
 SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id);

+ 165 - 27
3rdparty/spirv-cross/spirv_glsl.cpp

@@ -5102,8 +5102,27 @@ string CompilerGLSL::to_extract_constant_composite_expression(uint32_t result_ty
 	return constant_expression(tmp);
 	return constant_expression(tmp);
 }
 }
 
 
-string CompilerGLSL::to_rerolled_array_expression(const string &base_expr, const SPIRType &type)
+string CompilerGLSL::to_rerolled_array_expression(const SPIRType &parent_type,
+                                                  const string &base_expr, const SPIRType &type)
 {
 {
+	bool remapped_boolean = parent_type.basetype == SPIRType::Struct &&
+	                        type.basetype == SPIRType::Boolean &&
+	                        backend.boolean_in_struct_remapped_type != SPIRType::Boolean;
+
+	SPIRType tmp_type;
+	if (remapped_boolean)
+	{
+		tmp_type = get<SPIRType>(type.parent_type);
+		tmp_type.basetype = backend.boolean_in_struct_remapped_type;
+	}
+	else if (type.basetype == SPIRType::Boolean && backend.boolean_in_struct_remapped_type != SPIRType::Boolean)
+	{
+		// It's possible that we have an r-value expression that was OpLoaded from a struct.
+		// We have to reroll this and explicitly cast the input to bool, because the r-value is short.
+		tmp_type = get<SPIRType>(type.parent_type);
+		remapped_boolean = true;
+	}
+
 	uint32_t size = to_array_size_literal(type);
 	uint32_t size = to_array_size_literal(type);
 	auto &parent = get<SPIRType>(type.parent_type);
 	auto &parent = get<SPIRType>(type.parent_type);
 	string expr = "{ ";
 	string expr = "{ ";
@@ -5111,10 +5130,14 @@ string CompilerGLSL::to_rerolled_array_expression(const string &base_expr, const
 	for (uint32_t i = 0; i < size; i++)
 	for (uint32_t i = 0; i < size; i++)
 	{
 	{
 		auto subexpr = join(base_expr, "[", convert_to_string(i), "]");
 		auto subexpr = join(base_expr, "[", convert_to_string(i), "]");
-		if (parent.array.empty())
+		if (!type_is_top_level_array(parent))
+		{
+			if (remapped_boolean)
+				subexpr = join(type_to_glsl(tmp_type), "(", subexpr, ")");
 			expr += subexpr;
 			expr += subexpr;
+		}
 		else
 		else
-			expr += to_rerolled_array_expression(subexpr, parent);
+			expr += to_rerolled_array_expression(parent_type, subexpr, parent);
 
 
 		if (i + 1 < size)
 		if (i + 1 < size)
 			expr += ", ";
 			expr += ", ";
@@ -5124,13 +5147,26 @@ string CompilerGLSL::to_rerolled_array_expression(const string &base_expr, const
 	return expr;
 	return expr;
 }
 }
 
 
-string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool block_like_type)
+string CompilerGLSL::to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type)
 {
 {
 	auto &type = expression_type(id);
 	auto &type = expression_type(id);
 
 
-	bool reroll_array = !type.array.empty() &&
-	                    (!backend.array_is_value_type ||
-	                     (block_like_type && !backend.array_is_value_type_in_buffer_blocks));
+	bool reroll_array = false;
+	bool remapped_boolean = parent_type.basetype == SPIRType::Struct &&
+	                        type.basetype == SPIRType::Boolean &&
+	                        backend.boolean_in_struct_remapped_type != SPIRType::Boolean;
+
+	if (type_is_top_level_array(type))
+	{
+		reroll_array = !backend.array_is_value_type ||
+		               (block_like_type && !backend.array_is_value_type_in_buffer_blocks);
+
+		if (remapped_boolean)
+		{
+			// Forced to reroll if we have to change bool[] to short[].
+			reroll_array = true;
+		}
+	}
 
 
 	if (reroll_array)
 	if (reroll_array)
 	{
 	{
@@ -5144,10 +5180,20 @@ string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool block
 
 
 		// We're only triggering one read of the array expression, but this is fine since arrays have to be declared
 		// We're only triggering one read of the array expression, but this is fine since arrays have to be declared
 		// as temporaries anyways.
 		// as temporaries anyways.
-		return to_rerolled_array_expression(to_enclosed_expression(id), type);
+		return to_rerolled_array_expression(parent_type, to_enclosed_expression(id), type);
 	}
 	}
 	else
 	else
-		return to_unpacked_expression(id);
+	{
+		auto expr = to_unpacked_expression(id);
+		if (remapped_boolean)
+		{
+			auto tmp_type = type;
+			tmp_type.basetype = backend.boolean_in_struct_remapped_type;
+			expr = join(type_to_glsl(tmp_type), "(", expr, ")");
+		}
+
+		return expr;
+	}
 }
 }
 
 
 string CompilerGLSL::to_non_uniform_aware_expression(uint32_t id)
 string CompilerGLSL::to_non_uniform_aware_expression(uint32_t id)
@@ -5657,11 +5703,13 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
 	}
 	}
 }
 }
 
 
-string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_block_like_struct_scope)
+string CompilerGLSL::constant_expression(const SPIRConstant &c,
+                                         bool inside_block_like_struct_scope,
+                                         bool inside_struct_scope)
 {
 {
 	auto &type = get<SPIRType>(c.constant_type);
 	auto &type = get<SPIRType>(c.constant_type);
 
 
-	if (type.pointer)
+	if (type_is_top_level_pointer(type))
 	{
 	{
 		return backend.null_pointer_literal;
 		return backend.null_pointer_literal;
 	}
 	}
@@ -5676,19 +5724,32 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_bloc
 		// with Offset = 0, using no ArrayStride on the enclosed array type.
 		// with Offset = 0, using no ArrayStride on the enclosed array type.
 		// A particular CTS test hits this scenario.
 		// A particular CTS test hits this scenario.
 		bool array_type_decays = inside_block_like_struct_scope &&
 		bool array_type_decays = inside_block_like_struct_scope &&
-		                         !type.array.empty() && !backend.array_is_value_type_in_buffer_blocks;
+		                         type_is_top_level_array(type) &&
+		                         !backend.array_is_value_type_in_buffer_blocks;
 
 
 		// 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
 		bool needs_trailing_tracket = false;
 		bool needs_trailing_tracket = false;
 		if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct &&
 		if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct &&
-		    type.array.empty())
+		    !type_is_top_level_array(type))
 		{
 		{
 			res = type_to_glsl_constructor(type) + "{ ";
 			res = type_to_glsl_constructor(type) + "{ ";
 		}
 		}
 		else if (backend.use_initializer_list && backend.use_typed_initializer_list && backend.array_is_value_type &&
 		else if (backend.use_initializer_list && backend.use_typed_initializer_list && backend.array_is_value_type &&
-		         !type.array.empty() && !array_type_decays)
+		         type_is_top_level_array(type) && !array_type_decays)
 		{
 		{
-			res = type_to_glsl_constructor(type) + "({ ";
+			const auto *p_type = &type;
+			SPIRType tmp_type;
+
+			if (inside_struct_scope &&
+			    backend.boolean_in_struct_remapped_type != SPIRType::Boolean &&
+			    type.basetype == SPIRType::Boolean)
+			{
+				tmp_type = type;
+				tmp_type.basetype = backend.boolean_in_struct_remapped_type;
+				p_type = &tmp_type;
+			}
+
+			res = type_to_glsl_constructor(*p_type) + "({ ";
 			needs_trailing_tracket = true;
 			needs_trailing_tracket = true;
 		}
 		}
 		else if (backend.use_initializer_list)
 		else if (backend.use_initializer_list)
@@ -5718,7 +5779,7 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_bloc
 					res += to_name(elem);
 					res += to_name(elem);
 				else
 				else
 				{
 				{
-					if (type.array.empty() && type.basetype == SPIRType::Struct)
+					if (!type_is_top_level_array(type) && type.basetype == SPIRType::Struct)
 					{
 					{
 						// When we get down to emitting struct members, override the block-like information.
 						// When we get down to emitting struct members, override the block-like information.
 						// For constants, we can freely mix and match block-like state.
 						// For constants, we can freely mix and match block-like state.
@@ -5726,7 +5787,10 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_bloc
 						    has_member_decoration(type.self, subconstant_index, DecorationOffset);
 						    has_member_decoration(type.self, subconstant_index, DecorationOffset);
 					}
 					}
 
 
-					res += constant_expression(subc, inside_block_like_struct_scope);
+					if (type.basetype == SPIRType::Struct)
+						inside_struct_scope = true;
+
+					res += constant_expression(subc, inside_block_like_struct_scope, inside_struct_scope);
 				}
 				}
 			}
 			}
 
 
@@ -5748,19 +5812,30 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_bloc
 		if (backend.supports_empty_struct)
 		if (backend.supports_empty_struct)
 			return "{ }";
 			return "{ }";
 		else if (backend.use_typed_initializer_list)
 		else if (backend.use_typed_initializer_list)
-			return join(type_to_glsl(get<SPIRType>(c.constant_type)), "{ 0 }");
+			return join(type_to_glsl(type), "{ 0 }");
 		else if (backend.use_initializer_list)
 		else if (backend.use_initializer_list)
 			return "{ 0 }";
 			return "{ 0 }";
 		else
 		else
-			return join(type_to_glsl(get<SPIRType>(c.constant_type)), "(0)");
+			return join(type_to_glsl(type), "(0)");
 	}
 	}
 	else if (c.columns() == 1)
 	else if (c.columns() == 1)
 	{
 	{
-		return constant_expression_vector(c, 0);
+		auto res = constant_expression_vector(c, 0);
+
+		if (inside_struct_scope &&
+		    backend.boolean_in_struct_remapped_type != SPIRType::Boolean &&
+		    type.basetype == SPIRType::Boolean)
+		{
+			SPIRType tmp_type = type;
+			tmp_type.basetype = backend.boolean_in_struct_remapped_type;
+			res = join(type_to_glsl(tmp_type), "(", res, ")");
+		}
+
+		return res;
 	}
 	}
 	else
 	else
 	{
 	{
-		string res = type_to_glsl(get<SPIRType>(c.constant_type)) + "(";
+		string res = type_to_glsl(type) + "(";
 		for (uint32_t col = 0; col < c.columns(); col++)
 		for (uint32_t col = 0; col < c.columns(); col++)
 		{
 		{
 			if (c.specialization_constant_id(col) != 0)
 			if (c.specialization_constant_id(col) != 0)
@@ -5772,6 +5847,16 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c, bool inside_bloc
 				res += ", ";
 				res += ", ";
 		}
 		}
 		res += ")";
 		res += ")";
+
+		if (inside_struct_scope &&
+		    backend.boolean_in_struct_remapped_type != SPIRType::Boolean &&
+		    type.basetype == SPIRType::Boolean)
+		{
+			SPIRType tmp_type = type;
+			tmp_type.basetype = backend.boolean_in_struct_remapped_type;
+			res = join(type_to_glsl(tmp_type), "(", res, ")");
+		}
+
 		return res;
 		return res;
 	}
 	}
 }
 }
@@ -11107,7 +11192,7 @@ string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32
 
 
 			bool uses_buffer_offset =
 			bool uses_buffer_offset =
 			    type.basetype == SPIRType::Struct && has_member_decoration(type.self, i, DecorationOffset);
 			    type.basetype == SPIRType::Struct && has_member_decoration(type.self, i, DecorationOffset);
-			subop = to_composite_constructor_expression(elems[i], uses_buffer_offset);
+			subop = to_composite_constructor_expression(type, elems[i], uses_buffer_offset);
 		}
 		}
 
 
 		base = e ? e->base_expression : ID(0);
 		base = e ? e->base_expression : ID(0);
@@ -11197,8 +11282,15 @@ void CompilerGLSL::emit_block_instructions(SPIRBlock &block)
 	if (backend.requires_relaxed_precision_analysis)
 	if (backend.requires_relaxed_precision_analysis)
 	{
 	{
 		// If PHI variables are consumed in unexpected precision contexts, copy them here.
 		// If PHI variables are consumed in unexpected precision contexts, copy them here.
-		for (auto &phi : block.phi_variables)
+		for (size_t i = 0, n = block.phi_variables.size(); i < n; i++)
 		{
 		{
+			auto &phi = block.phi_variables[i];
+
+			// Ensure we only copy once. We know a-priori that this array will lay out
+			// the same function variables together.
+			if (i && block.phi_variables[i - 1].function_variable == phi.function_variable)
+				continue;
+
 			auto itr = temporary_to_mirror_precision_alias.find(phi.function_variable);
 			auto itr = temporary_to_mirror_precision_alias.find(phi.function_variable);
 			if (itr != temporary_to_mirror_precision_alias.end())
 			if (itr != temporary_to_mirror_precision_alias.end())
 			{
 			{
@@ -11737,7 +11829,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 			// it is an array, and our backend does not support arrays as value types.
 			// it is an array, and our backend does not support arrays as value types.
 			// Emit the temporary, and copy it explicitly.
 			// Emit the temporary, and copy it explicitly.
 			e = &emit_uninitialized_temporary_expression(result_type, id);
 			e = &emit_uninitialized_temporary_expression(result_type, id);
-			emit_array_copy(to_expression(id), id, ptr, StorageClassFunction, get_expression_effective_storage_class(ptr));
+			emit_array_copy(nullptr, id, ptr, StorageClassFunction, get_expression_effective_storage_class(ptr));
 		}
 		}
 		else
 		else
 			e = &emit_op(result_type, id, expr, forward, !usage_tracking);
 			e = &emit_op(result_type, id, expr, forward, !usage_tracking);
@@ -15565,6 +15657,11 @@ void CompilerGLSL::require_extension(const std::string &ext)
 		forced_extensions.push_back(ext);
 		forced_extensions.push_back(ext);
 }
 }
 
 
+const SmallVector<std::string> &CompilerGLSL::get_required_extensions() const
+{
+	return forced_extensions;
+}
+
 void CompilerGLSL::require_extension_internal(const string &ext)
 void CompilerGLSL::require_extension_internal(const string &ext)
 {
 {
 	if (backend.supports_extensions && !has_extension(ext))
 	if (backend.supports_extensions && !has_extension(ext))
@@ -16329,6 +16426,17 @@ bool CompilerGLSL::for_loop_initializers_are_same_type(const SPIRBlock &block)
 	return true;
 	return true;
 }
 }
 
 
+void CompilerGLSL::emit_block_instructions_with_masked_debug(SPIRBlock &block)
+{
+	// Have to block debug instructions such as OpLine here, since it will be treated as a statement otherwise,
+	// which breaks loop optimizations.
+	// Any line directive would be declared outside the loop body, which would just be confusing either way.
+	bool old_block_debug_directives = block_debug_directives;
+	block_debug_directives = true;
+	emit_block_instructions(block);
+	block_debug_directives = old_block_debug_directives;
+}
+
 bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method)
 bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method)
 {
 {
 	SPIRBlock::ContinueBlockType continue_type = continue_block_type(get<SPIRBlock>(block.continue_block));
 	SPIRBlock::ContinueBlockType continue_type = continue_block_type(get<SPIRBlock>(block.continue_block));
@@ -16339,7 +16447,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
 		// If we're trying to create a true for loop,
 		// If we're trying to create a true for loop,
 		// we need to make sure that all opcodes before branch statement do not actually emit any code.
 		// we need to make sure that all opcodes before branch statement do not actually emit any code.
 		// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
 		// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
-		emit_block_instructions(block);
+		emit_block_instructions_with_masked_debug(block);
 
 
 		bool condition_is_temporary = forced_temporaries.find(block.condition) == end(forced_temporaries);
 		bool condition_is_temporary = forced_temporaries.find(block.condition) == end(forced_temporaries);
 
 
@@ -16419,7 +16527,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
 		// If we're trying to create a true for loop,
 		// If we're trying to create a true for loop,
 		// we need to make sure that all opcodes before branch statement do not actually emit any code.
 		// we need to make sure that all opcodes before branch statement do not actually emit any code.
 		// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
 		// We can then take the condition expression and create a for (; cond ; ) { body; } structure instead.
-		emit_block_instructions(child);
+		emit_block_instructions_with_masked_debug(child);
 
 
 		bool condition_is_temporary = forced_temporaries.find(child.condition) == end(forced_temporaries);
 		bool condition_is_temporary = forced_temporaries.find(child.condition) == end(forced_temporaries);
 
 
@@ -16557,6 +16665,24 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
 	if (block.merge == SPIRBlock::MergeLoop)
 	if (block.merge == SPIRBlock::MergeLoop)
 		add_loop_level();
 		add_loop_level();
 
 
+	// If we're emitting PHI variables with precision aliases, we have to emit them as hoisted temporaries.
+	for (auto var_id : block.dominated_variables)
+	{
+		auto &var = get<SPIRVariable>(var_id);
+		if (var.phi_variable)
+		{
+			auto mirrored_precision_itr = temporary_to_mirror_precision_alias.find(var_id);
+			if (mirrored_precision_itr != temporary_to_mirror_precision_alias.end() &&
+			    find_if(block.declare_temporary.begin(), block.declare_temporary.end(),
+			            [mirrored_precision_itr](const std::pair<TypeID, VariableID> &p) {
+			              return p.second == mirrored_precision_itr->second;
+			            }) == block.declare_temporary.end())
+			{
+				block.declare_temporary.push_back({ var.basetype, mirrored_precision_itr->second });
+			}
+		}
+	}
+
 	emit_hoisted_temporaries(block.declare_temporary);
 	emit_hoisted_temporaries(block.declare_temporary);
 
 
 	SPIRBlock::ContinueBlockType continue_type = SPIRBlock::ContinueNone;
 	SPIRBlock::ContinueBlockType continue_type = SPIRBlock::ContinueNone;
@@ -17338,9 +17464,16 @@ uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
 	                    MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
 	                    MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
 }
 }
 
 
-void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t, uint32_t rhs_id, StorageClass, StorageClass)
+bool CompilerGLSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, StorageClass, StorageClass)
 {
 {
+	string lhs;
+	if (expr)
+		lhs = expr;
+	else
+		lhs = to_expression(lhs_id);
+
 	statement(lhs, " = ", to_expression(rhs_id), ";");
 	statement(lhs, " = ", to_expression(rhs_id), ";");
+	return true;
 }
 }
 
 
 bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id)
 bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id)
@@ -17773,6 +17906,11 @@ void CompilerGLSL::emit_line_directive(uint32_t file_id, uint32_t line_literal)
 	if (redirect_statement)
 	if (redirect_statement)
 		return;
 		return;
 
 
+	// If we're emitting code in a sensitive context such as condition blocks in for loops, don't emit
+	// any line directives, because it's not possible.
+	if (block_debug_directives)
+		return;
+
 	if (options.emit_line_directives)
 	if (options.emit_line_directives)
 	{
 	{
 		require_extension_internal("GL_GOOGLE_cpp_style_line_directive");
 		require_extension_internal("GL_GOOGLE_cpp_style_line_directive");

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

@@ -258,6 +258,10 @@ public:
 	// require_extension("GL_KHR_my_extension");
 	// require_extension("GL_KHR_my_extension");
 	void require_extension(const std::string &ext);
 	void require_extension(const std::string &ext);
 
 
+	// Returns the list of required extensions. After compilation this will contains any other 
+	// extensions that the compiler used automatically, in addition to the user specified ones.
+	const SmallVector<std::string> &get_required_extensions() const;
+
 	// Legacy GLSL compatibility method.
 	// Legacy GLSL compatibility method.
 	// Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead.
 	// Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead.
 	// For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but
 	// For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but
@@ -393,6 +397,7 @@ protected:
 	};
 	};
 	TemporaryCopy handle_instruction_precision(const Instruction &instr);
 	TemporaryCopy handle_instruction_precision(const Instruction &instr);
 	void emit_block_instructions(SPIRBlock &block);
 	void emit_block_instructions(SPIRBlock &block);
+	void emit_block_instructions_with_masked_debug(SPIRBlock &block);
 
 
 	// For relax_nan_checks.
 	// For relax_nan_checks.
 	GLSLstd450 get_remapped_glsl_op(GLSLstd450 std450_op) const;
 	GLSLstd450 get_remapped_glsl_op(GLSLstd450 std450_op) const;
@@ -426,7 +431,9 @@ protected:
 	                                const std::string &qualifier = "", uint32_t base_offset = 0);
 	                                const std::string &qualifier = "", uint32_t base_offset = 0);
 	virtual void emit_struct_padding_target(const SPIRType &type);
 	virtual void emit_struct_padding_target(const SPIRType &type);
 	virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
 	virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
-	std::string constant_expression(const SPIRConstant &c, bool inside_block_like_struct_scope = false);
+	std::string constant_expression(const SPIRConstant &c,
+	                                bool inside_block_like_struct_scope = false,
+	                                bool inside_struct_scope = false);
 	virtual std::string constant_op_expression(const SPIRConstantOp &cop);
 	virtual std::string constant_op_expression(const SPIRConstantOp &cop);
 	virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
 	virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
 	virtual void emit_fixup();
 	virtual void emit_fixup();
@@ -539,6 +546,7 @@ protected:
 	SmallVector<std::string> *redirect_statement = nullptr;
 	SmallVector<std::string> *redirect_statement = nullptr;
 	const SPIRBlock *current_continue_block = nullptr;
 	const SPIRBlock *current_continue_block = nullptr;
 	bool block_temporary_hoisting = false;
 	bool block_temporary_hoisting = false;
+	bool block_debug_directives = false;
 
 
 	void begin_scope();
 	void begin_scope();
 	void end_scope();
 	void end_scope();
@@ -604,6 +612,7 @@ protected:
 		const char *uint16_t_literal_suffix = "us";
 		const char *uint16_t_literal_suffix = "us";
 		const char *nonuniform_qualifier = "nonuniformEXT";
 		const char *nonuniform_qualifier = "nonuniformEXT";
 		const char *boolean_mix_function = "mix";
 		const char *boolean_mix_function = "mix";
+		SPIRType::BaseType boolean_in_struct_remapped_type = SPIRType::Boolean;
 		bool swizzle_is_function = false;
 		bool swizzle_is_function = false;
 		bool shared_is_implied = false;
 		bool shared_is_implied = false;
 		bool unsized_array_supported = true;
 		bool unsized_array_supported = true;
@@ -772,8 +781,8 @@ protected:
 	void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist);
 	void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist);
 	std::string to_non_uniform_aware_expression(uint32_t id);
 	std::string to_non_uniform_aware_expression(uint32_t id);
 	std::string to_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_expression(uint32_t id, bool register_expression_read = true);
-	std::string to_composite_constructor_expression(uint32_t id, bool block_like_type);
-	std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type);
+	std::string to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type);
+	std::string to_rerolled_array_expression(const SPIRType &parent_type, const std::string &expr, const SPIRType &type);
 	std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_unpacked_row_major_matrix_expression(uint32_t id);
 	std::string to_unpacked_row_major_matrix_expression(uint32_t id);
@@ -806,7 +815,7 @@ protected:
 	std::string layout_for_variable(const SPIRVariable &variable);
 	std::string layout_for_variable(const SPIRVariable &variable);
 	std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id);
 	std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id);
 	virtual bool skip_argument(uint32_t id) const;
 	virtual bool skip_argument(uint32_t id) const;
-	virtual void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
+	virtual bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id,
 	                             spv::StorageClass lhs_storage, spv::StorageClass rhs_storage);
 	                             spv::StorageClass lhs_storage, spv::StorageClass rhs_storage);
 	virtual void emit_block_hints(const SPIRBlock &block);
 	virtual void emit_block_hints(const SPIRBlock &block);
 	virtual std::string to_initializer_expression(const SPIRVariable &var);
 	virtual std::string to_initializer_expression(const SPIRVariable &var);

+ 59 - 24
3rdparty/spirv-cross/spirv_msl.cpp

@@ -1467,6 +1467,7 @@ string CompilerMSL::compile()
 	backend.support_small_type_sampling_result = true;
 	backend.support_small_type_sampling_result = true;
 	backend.supports_empty_struct = true;
 	backend.supports_empty_struct = true;
 	backend.support_64bit_switch = true;
 	backend.support_64bit_switch = true;
+	backend.boolean_in_struct_remapped_type = SPIRType::Short;
 
 
 	// Allow Metal to use the array<T> template unless we force it off.
 	// Allow Metal to use the array<T> template unless we force it off.
 	backend.can_return_array = !msl_options.force_native_arrays;
 	backend.can_return_array = !msl_options.force_native_arrays;
@@ -7234,7 +7235,7 @@ void CompilerMSL::declare_constant_arrays()
 		// FIXME: However, hoisting constants to main() means we need to pass down constant arrays to leaf functions if they are used there.
 		// FIXME: However, hoisting constants to main() means we need to pass down constant arrays to leaf functions if they are used there.
 		// If there are multiple functions in the module, drop this case to avoid breaking use cases which do not need to
 		// If there are multiple functions in the module, drop this case to avoid breaking use cases which do not need to
 		// link into Metal libraries. This is hacky.
 		// link into Metal libraries. This is hacky.
-		if (!type.array.empty() && (!fully_inlined || is_scalar(type) || is_vector(type)))
+		if (type_is_top_level_array(type) && (!fully_inlined || is_scalar(type) || is_vector(type)))
 		{
 		{
 			add_resource_name(c.self);
 			add_resource_name(c.self);
 			auto name = to_name(c.self);
 			auto name = to_name(c.self);
@@ -7266,7 +7267,7 @@ void CompilerMSL::declare_complex_constant_arrays()
 			return;
 			return;
 
 
 		auto &type = this->get<SPIRType>(c.constant_type);
 		auto &type = this->get<SPIRType>(c.constant_type);
-		if (!type.array.empty() && !(is_scalar(type) || is_vector(type)))
+		if (type_is_top_level_array(type) && !(is_scalar(type) || is_vector(type)))
 		{
 		{
 			add_resource_name(c.self);
 			add_resource_name(c.self);
 			auto name = to_name(c.self);
 			auto name = to_name(c.self);
@@ -7376,11 +7377,6 @@ void CompilerMSL::emit_specialization_constants_and_structs()
 				string sc_type_name = type_to_glsl(type);
 				string sc_type_name = type_to_glsl(type);
 				add_resource_name(c.self);
 				add_resource_name(c.self);
 				string sc_name = to_name(c.self);
 				string sc_name = to_name(c.self);
-				uint32_t constant_id = get_decoration(c.self, DecorationSpecId);
-				if (!unique_func_constants.count(constant_id))
-					unique_func_constants.insert(make_pair(constant_id, c.self));
-				SPIRType::BaseType sc_tmp_type = expression_type(unique_func_constants[constant_id]).basetype;
-				string sc_tmp_name = to_name(unique_func_constants[constant_id]) + "_tmp";
 
 
 				// Function constants are only supported in MSL 1.2 and later.
 				// Function constants are only supported in MSL 1.2 and later.
 				// If we don't support it just declare the "default" directly.
 				// If we don't support it just declare the "default" directly.
@@ -7391,6 +7387,11 @@ void CompilerMSL::emit_specialization_constants_and_structs()
 				    !c.is_used_as_array_length)
 				    !c.is_used_as_array_length)
 				{
 				{
 					// Only scalar, non-composite values can be function constants.
 					// Only scalar, non-composite values can be function constants.
+					uint32_t constant_id = get_decoration(c.self, DecorationSpecId);
+					if (!unique_func_constants.count(constant_id))
+						unique_func_constants.insert(make_pair(constant_id, c.self));
+					SPIRType::BaseType sc_tmp_type = expression_type(unique_func_constants[constant_id]).basetype;
+					string sc_tmp_name = to_name(unique_func_constants[constant_id]) + "_tmp";
 					if (unique_func_constants[constant_id] == c.self)
 					if (unique_func_constants[constant_id] == c.self)
 						statement("constant ", sc_type_name, " ", sc_tmp_name, " [[function_constant(", constant_id,
 						statement("constant ", sc_type_name, " ", sc_tmp_name, " [[function_constant(", constant_id,
 						          ")]];");
 						          ")]];");
@@ -9469,7 +9470,7 @@ static bool storage_class_array_is_thread(StorageClass storage)
 	}
 	}
 }
 }
 
 
-void CompilerMSL::emit_array_copy(const string &lhs, uint32_t lhs_id, uint32_t rhs_id,
+bool CompilerMSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id,
 								  StorageClass lhs_storage, StorageClass rhs_storage)
 								  StorageClass lhs_storage, StorageClass rhs_storage)
 {
 {
 	// 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.
@@ -9508,10 +9509,21 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t lhs_id, uint32_t r
 	// Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier.
 	// Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier.
 	if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array())
 	if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array())
 	{
 	{
-		statement(lhs, " = ", to_expression(rhs_id), ";");
+		// Fall back to normal copy path.
+		return false;
 	}
 	}
 	else
 	else
 	{
 	{
+		// Ensure the LHS variable has been declared
+		if (lhs_var)
+			flush_variable_declaration(lhs_var->self);
+
+		string lhs;
+		if (expr)
+			lhs = expr;
+		else
+			lhs = to_expression(lhs_id);
+
 		// Assignment from an array initializer is fine.
 		// Assignment from an array initializer is fine.
 		auto &type = expression_type(rhs_id);
 		auto &type = expression_type(rhs_id);
 		auto *var = maybe_get_backing_variable(rhs_id);
 		auto *var = maybe_get_backing_variable(rhs_id);
@@ -9585,6 +9597,8 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t lhs_id, uint32_t r
 		else
 		else
 			statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
 			statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
 	}
 	}
+
+	return true;
 }
 }
 
 
 uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const
 uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const
@@ -9641,14 +9655,11 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
 		}
 		}
 	}
 	}
 
 
-	// Ensure the LHS variable has been declared
-	auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
-	if (p_v_lhs)
-		flush_variable_declaration(p_v_lhs->self);
-
 	auto lhs_storage = get_expression_effective_storage_class(id_lhs);
 	auto lhs_storage = get_expression_effective_storage_class(id_lhs);
 	auto rhs_storage = get_expression_effective_storage_class(id_rhs);
 	auto rhs_storage = get_expression_effective_storage_class(id_rhs);
-	emit_array_copy(to_expression(id_lhs), id_lhs, id_rhs, lhs_storage, rhs_storage);
+	if (!emit_array_copy(nullptr, id_lhs, id_rhs, lhs_storage, rhs_storage))
+		return false;
+
 	register_write(id_lhs);
 	register_write(id_lhs);
 
 
 	return true;
 	return true;
@@ -16784,20 +16795,31 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr,
 	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 = nullptr, *phys_type = nullptr;
 	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
 		phys_type = &expr_type;
 		phys_type = &expr_type;
+
 	if (var)
 	if (var)
 	{
 	{
 		source_id = var->self;
 		source_id = var->self;
 		var_type = &get_variable_data_type(*var);
 		var_type = &get_variable_data_type(*var);
 	}
 	}
 
 
+	bool rewrite_boolean_load =
+	    expr_type.basetype == SPIRType::Boolean &&
+	    (var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct));
+
 	// Type fixups for workgroup variables if they are booleans.
 	// Type fixups for workgroup variables if they are booleans.
-	if (var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct) &&
-	    expr_type.basetype == SPIRType::Boolean)
-		expr = join(type_to_glsl(expr_type), "(", expr, ")");
+	if (rewrite_boolean_load)
+	{
+		if (type_is_top_level_array(expr_type))
+			expr = to_rerolled_array_expression(expr_type, expr, expr_type);
+		else
+			expr = join(type_to_glsl(expr_type), "(", expr, ")");
+	}
+
 	// Type fixups for workgroup variables if they are matrices.
 	// Type fixups for workgroup variables if they are matrices.
 	// Don't do fixup for packed types; those are handled specially.
 	// Don't do fixup for packed types; those are handled specially.
 	// FIXME: Maybe use a type like spvStorageMatrix for packed matrices?
 	// FIXME: Maybe use a type like spvStorageMatrix for packed matrices?
@@ -16910,24 +16932,37 @@ void CompilerMSL::cast_to_variable_store(uint32_t target_id, std::string &expr,
 	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 = nullptr, *phys_type = nullptr;
 	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
 		phys_type = &expr_type;
 		phys_type = &expr_type;
+
 	if (var)
 	if (var)
 	{
 	{
 		target_id = var->self;
 		target_id = var->self;
 		var_type = &get_variable_data_type(*var);
 		var_type = &get_variable_data_type(*var);
 	}
 	}
 
 
-	// Type fixups for workgroup variables if they are booleans.
-	if (var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct) &&
-	    expr_type.basetype == SPIRType::Boolean)
+	bool rewrite_boolean_store =
+		expr_type.basetype == SPIRType::Boolean &&
+		(var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct));
+
+	// Type fixups for workgroup variables or struct members if they are booleans.
+	if (rewrite_boolean_store)
 	{
 	{
-		auto short_type = expr_type;
-		short_type.basetype = SPIRType::Short;
-		expr = join(type_to_glsl(short_type), "(", expr, ")");
+		if (type_is_top_level_array(expr_type))
+		{
+			expr = to_rerolled_array_expression(*var_type, expr, expr_type);
+		}
+		else
+		{
+			auto short_type = expr_type;
+			short_type.basetype = SPIRType::Short;
+			expr = join(type_to_glsl(short_type), "(", expr, ")");
+		}
 	}
 	}
+
 	// Type fixups for workgroup variables if they are matrices.
 	// Type fixups for workgroup variables if they are matrices.
 	// Don't do fixup for packed types; those are handled specially.
 	// Don't do fixup for packed types; those are handled specially.
 	// FIXME: Maybe use a type like spvStorageMatrix for packed matrices?
 	// FIXME: Maybe use a type like spvStorageMatrix for packed matrices?

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

@@ -1032,7 +1032,7 @@ protected:
 	void add_pragma_line(const std::string &line);
 	void add_pragma_line(const std::string &line);
 	void add_typedef_line(const std::string &line);
 	void add_typedef_line(const std::string &line);
 	void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
 	void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
-	void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
+	bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id,
 	                     spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override;
 	                     spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override;
 	void build_implicit_builtins();
 	void build_implicit_builtins();
 	uint32_t build_constant_uint_array_pointer();
 	uint32_t build_constant_uint_array_pointer();