Jelajahi Sumber

Updated spirv-cross.

Бранимир Караџић 5 tahun lalu
induk
melakukan
a24274738e

+ 21 - 1
3rdparty/spirv-cross/main.cpp

@@ -569,6 +569,7 @@ struct CLIArguments
 	SmallVector<uint32_t> msl_device_argument_buffers;
 	SmallVector<uint32_t> msl_device_argument_buffers;
 	SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
 	SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
 	SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
 	SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
+	SmallVector<MSLShaderInput> msl_shader_inputs;
 	SmallVector<PLSArg> pls_in;
 	SmallVector<PLSArg> pls_in;
 	SmallVector<PLSArg> pls_out;
 	SmallVector<PLSArg> pls_out;
 	SmallVector<Remap> remaps;
 	SmallVector<Remap> remaps;
@@ -738,7 +739,10 @@ static void print_help_msl()
 	                "\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n"
 	                "\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n"
 	                "\t[--msl-enable-frag-output-mask <mask>]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n"
 	                "\t[--msl-enable-frag-output-mask <mask>]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n"
 	                "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n"
 	                "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n"
-	);
+	                "\t[--msl-shader-input <index> <format> <size>]:\n\t\tSpecify the format of the shader input at <index>.\n"
+	                "\t\t<format> can be 'u16', 'u8', or 'other', to indicate a 16-bit unsigned integer, 8-bit unsigned integer, "
+	                "or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
+	                "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n");
 }
 }
 
 
 static void print_help_common()
 static void print_help_common()
@@ -975,6 +979,8 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
 			msl_comp->add_dynamic_buffer(v.first, v.second, i++);
 			msl_comp->add_dynamic_buffer(v.first, v.second, i++);
 		for (auto &v : args.msl_inline_uniform_blocks)
 		for (auto &v : args.msl_inline_uniform_blocks)
 			msl_comp->add_inline_uniform_block(v.first, v.second);
 			msl_comp->add_inline_uniform_block(v.first, v.second);
+		for (auto &v : args.msl_shader_inputs)
+			msl_comp->add_msl_shader_input(v);
 	}
 	}
 	else if (args.hlsl)
 	else if (args.hlsl)
 		compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
 		compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
@@ -1356,6 +1362,20 @@ static int main_inner(int argc, char *argv[])
 	        [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
 	        [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
 	cbs.add("--msl-no-clip-distance-user-varying",
 	cbs.add("--msl-no-clip-distance-user-varying",
 	        [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
 	        [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
+	cbs.add("--msl-shader-input", [&args](CLIParser &parser) {
+		MSLShaderInput input;
+		// Make sure next_uint() is called in-order.
+		input.location = parser.next_uint();
+		const char *format = parser.next_value_string("other");
+		if (strcmp(format, "u16") == 0)
+			input.format = MSL_VERTEX_FORMAT_UINT16;
+		else if (strcmp(format, "u8") == 0)
+			input.format = MSL_VERTEX_FORMAT_UINT8;
+		else
+			input.format = MSL_VERTEX_FORMAT_OTHER;
+		input.vecsize = parser.next_uint();
+		args.msl_shader_inputs.push_back(input);
+	});
 	cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
 	cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
 	cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
 	cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
 		auto old_name = parser.next_string();
 		auto old_name = parser.next_string();

+ 19 - 3
3rdparty/spirv-cross/spirv_cross.cpp

@@ -273,11 +273,27 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
 	return var;
 	return var;
 }
 }
 
 
-StorageClass Compiler::get_backing_variable_storage(uint32_t ptr)
+StorageClass Compiler::get_expression_effective_storage_class(uint32_t ptr)
 {
 {
 	auto *var = maybe_get_backing_variable(ptr);
 	auto *var = maybe_get_backing_variable(ptr);
-	if (var)
-		return var->storage;
+
+	// If the expression has been lowered to a temporary, we need to use the Generic storage class.
+	// We're looking for the effective storage class of a given expression.
+	// An access chain or forwarded OpLoads from such access chains
+	// will generally have the storage class of the underlying variable, but if the load was not forwarded
+	// we have lost any address space qualifiers.
+	bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression &&
+	                        !get<SPIRExpression>(ptr).access_chain &&
+	                        (forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0);
+
+	if (var && !forced_temporary)
+	{
+		// Normalize SSBOs to StorageBuffer here.
+		if (var->storage == StorageClassUniform && has_decoration(get<SPIRType>(var->basetype).self, DecorationBufferBlock))
+			return StorageClassStorageBuffer;
+		else
+			return var->storage;
+	}
 	else
 	else
 		return expression_type(ptr).storage;
 		return expression_type(ptr).storage;
 }
 }

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

@@ -611,7 +611,7 @@ protected:
 	bool expression_is_lvalue(uint32_t id) const;
 	bool expression_is_lvalue(uint32_t id) const;
 	bool variable_storage_is_aliased(const SPIRVariable &var);
 	bool variable_storage_is_aliased(const SPIRVariable &var);
 	SPIRVariable *maybe_get_backing_variable(uint32_t chain);
 	SPIRVariable *maybe_get_backing_variable(uint32_t chain);
-	spv::StorageClass get_backing_variable_storage(uint32_t ptr);
+	spv::StorageClass get_expression_effective_storage_class(uint32_t ptr);
 
 
 	void register_read(uint32_t expr, uint32_t chain, bool forwarded);
 	void register_read(uint32_t expr, uint32_t chain, bool forwarded);
 	void register_write(uint32_t chain);
 	void register_write(uint32_t chain);

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

@@ -1032,6 +1032,30 @@ spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const
 #endif
 #endif
 }
 }
 
 
+spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_input *si)
+{
+#if SPIRV_CROSS_C_API_MSL
+	if (compiler->backend != SPVC_BACKEND_MSL)
+	{
+		compiler->context->report_error("MSL function used on a non-MSL backend.");
+		return SPVC_ERROR_INVALID_ARGUMENT;
+	}
+
+	auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
+	MSLShaderInput input;
+	input.location = si->location;
+	input.format = static_cast<MSLShaderInputFormat>(si->format);
+	input.builtin = static_cast<spv::BuiltIn>(si->builtin);
+	input.vecsize = si->vecsize;
+	msl.add_msl_shader_input(input);
+	return SPVC_SUCCESS;
+#else
+	(void)si;
+	compiler->context->report_error("MSL function used on a non-MSL backend.");
+	return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
 spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
 spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
                                                    const spvc_msl_resource_binding *binding)
                                                    const spvc_msl_resource_binding *binding)
 {
 {
@@ -2267,6 +2291,19 @@ void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr)
 #endif
 #endif
 }
 }
 
 
+void spvc_msl_shader_input_init(spvc_msl_shader_input *input)
+{
+#if SPIRV_CROSS_C_API_MSL
+	MSLShaderInput input_default;
+	input->location = input_default.location;
+	input->format = static_cast<spvc_msl_shader_input_format>(input_default.format);
+	input->builtin = static_cast<SpvBuiltIn>(input_default.builtin);
+	input->vecsize = input_default.vecsize;
+#else
+	memset(input, 0, sizeof(*input));
+#endif
+}
+
 void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
 void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
 {
 {
 #if SPIRV_CROSS_C_API_MSL
 #if SPIRV_CROSS_C_API_MSL

+ 28 - 7
3rdparty/spirv-cross/spirv_cross_c.h

@@ -33,7 +33,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 34
+#define SPVC_C_API_VERSION_MINOR 35
 /* 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
 
 
@@ -259,14 +259,19 @@ typedef enum spvc_msl_platform
 } spvc_msl_platform;
 } spvc_msl_platform;
 
 
 /* Maps to C++ API. */
 /* Maps to C++ API. */
-typedef enum spvc_msl_vertex_format
+typedef enum spvc_msl_shader_input_format
 {
 {
-	SPVC_MSL_VERTEX_FORMAT_OTHER = 0,
-	SPVC_MSL_VERTEX_FORMAT_UINT8 = 1,
-	SPVC_MSL_VERTEX_FORMAT_UINT16 = 2
-} spvc_msl_vertex_format;
+	SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = 0,
+	SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
+	SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
 
 
-/* Maps to C++ API. */
+	/* Deprecated names. */
+	SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_INPUT_FORMAT_OTHER,
+	SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT8,
+	SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT16
+} spvc_msl_shader_input_format, spvc_msl_vertex_format;
+
+/* Maps to C++ API. Deprecated; use spvc_msl_shader_input. */
 typedef struct spvc_msl_vertex_attribute
 typedef struct spvc_msl_vertex_attribute
 {
 {
 	unsigned location;
 	unsigned location;
@@ -289,6 +294,20 @@ typedef struct spvc_msl_vertex_attribute
  */
  */
 SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
 SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
 
 
+/* Maps to C++ API. */
+typedef struct spvc_msl_shader_input
+{
+	unsigned location;
+	spvc_msl_vertex_format format;
+	SpvBuiltIn builtin;
+	unsigned vecsize;
+} spvc_msl_shader_input;
+
+/*
+ * Initializes the shader input struct.
+ */
+SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input);
+
 /* Maps to C++ API. */
 /* Maps to C++ API. */
 typedef struct spvc_msl_resource_binding
 typedef struct spvc_msl_resource_binding
 {
 {
@@ -698,6 +717,8 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler
                                                                    const spvc_msl_vertex_attribute *attrs);
                                                                    const spvc_msl_vertex_attribute *attrs);
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
                                                                    const spvc_msl_resource_binding *binding);
                                                                    const spvc_msl_resource_binding *binding);
+SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler,
+                                                               const spvc_msl_shader_input *input);
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set);
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set);
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address);
 SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address);
 SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);
 SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);

+ 54 - 9
3rdparty/spirv-cross/spirv_glsl.cpp

@@ -3428,10 +3428,15 @@ string CompilerGLSL::to_rerolled_array_expression(const string &base_expr, const
 	return expr;
 	return expr;
 }
 }
 
 
-string CompilerGLSL::to_composite_constructor_expression(uint32_t id)
+string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset)
 {
 {
 	auto &type = expression_type(id);
 	auto &type = expression_type(id);
-	if (!backend.array_is_value_type && !type.array.empty())
+
+	bool reroll_array = !type.array.empty() &&
+	                    (!backend.array_is_value_type ||
+	                     (uses_buffer_offset && !backend.buffer_offset_array_is_value_type));
+
+	if (reroll_array)
 	{
 	{
 		// For this case, we need to "re-roll" an array initializer from a temporary.
 		// For this case, we need to "re-roll" an array initializer from a temporary.
 		// We cannot simply pass the array directly, since it decays to a pointer and it cannot
 		// We cannot simply pass the array directly, since it decays to a pointer and it cannot
@@ -5689,6 +5694,25 @@ bool CompilerGLSL::expression_is_constant_null(uint32_t id) const
 	return c->constant_is_null();
 	return c->constant_is_null();
 }
 }
 
 
+bool CompilerGLSL::expression_is_non_value_type_array(uint32_t ptr)
+{
+	auto &type = expression_type(ptr);
+	if (type.array.empty())
+		return false;
+
+	if (!backend.array_is_value_type)
+		return true;
+
+	auto *var = maybe_get_backing_variable(ptr);
+	if (!var)
+		return false;
+
+	auto &backed_type = get<SPIRType>(var->basetype);
+	return !backend.buffer_offset_array_is_value_type &&
+	       backed_type.basetype == SPIRType::Struct &&
+	       has_member_decoration(backed_type.self, 0, DecorationOffset);
+}
+
 // Returns the function name for a texture sampling function for the specified image and sampling characteristics.
 // Returns the function name for a texture sampling function for the specified image and sampling characteristics.
 // For some subclasses, the function is a method on the specified image.
 // For some subclasses, the function is a method on the specified image.
 string CompilerGLSL::to_function_name(const TextureFunctionNameArguments &args)
 string CompilerGLSL::to_function_name(const TextureFunctionNameArguments &args)
@@ -6973,6 +6997,10 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
 					"Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
 					"Cannot implement gl_InstanceID in Vulkan GLSL. This shader was created with GL semantics.");
 			}
 			}
 		}
 		}
+		if (!options.es && options.version < 140)
+		{
+			require_extension_internal("GL_ARB_draw_instanced");
+		}
 		return "gl_InstanceID";
 		return "gl_InstanceID";
 	case BuiltInVertexIndex:
 	case BuiltInVertexIndex:
 		if (options.vulkan_semantics)
 		if (options.vulkan_semantics)
@@ -6982,7 +7010,13 @@ string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
 	case BuiltInInstanceIndex:
 	case BuiltInInstanceIndex:
 		if (options.vulkan_semantics)
 		if (options.vulkan_semantics)
 			return "gl_InstanceIndex";
 			return "gl_InstanceIndex";
-		else if (options.vertex.support_nonzero_base_instance)
+
+		if (!options.es && options.version < 140)
+		{
+			require_extension_internal("GL_ARB_draw_instanced");
+		}
+
+		if (options.vertex.support_nonzero_base_instance)
 		{
 		{
 			if (!options.vulkan_semantics)
 			if (!options.vulkan_semantics)
 			{
 			{
@@ -8400,7 +8434,10 @@ string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32
 
 
 			if (i)
 			if (i)
 				op += ", ";
 				op += ", ";
-			subop = to_composite_constructor_expression(elems[i]);
+
+			bool uses_buffer_offset = type.basetype == SPIRType::Struct &&
+			                          has_member_decoration(type.self, i, DecorationOffset);
+			subop = to_composite_constructor_expression(elems[i], uses_buffer_offset);
 		}
 		}
 
 
 		base = e ? e->base_expression : ID(0);
 		base = e ? e->base_expression : ID(0);
@@ -8687,15 +8724,23 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 			expr = to_unpacked_expression(ptr);
 			expr = to_unpacked_expression(ptr);
 		}
 		}
 
 
+		auto &type = get<SPIRType>(result_type);
+		auto &expr_type = expression_type(ptr);
+
+		// If the expression has more vector components than the result type, insert
+		// a swizzle. This shouldn't happen normally on valid SPIR-V, but it might
+		// happen with e.g. the MSL backend replacing the type of an input variable.
+		if (expr_type.vecsize > type.vecsize)
+			expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0));
+
 		// We might need to bitcast in order to load from a builtin.
 		// We might need to bitcast in order to load from a builtin.
-		bitcast_from_builtin_load(ptr, expr, get<SPIRType>(result_type));
+		bitcast_from_builtin_load(ptr, expr, type);
 
 
 		// We might be trying to load a gl_Position[N], where we should be
 		// We might be trying to load a gl_Position[N], where we should be
 		// doing float4[](gl_in[i].gl_Position, ...) instead.
 		// doing float4[](gl_in[i].gl_Position, ...) instead.
 		// Similar workarounds are required for input arrays in tessellation.
 		// Similar workarounds are required for input arrays in tessellation.
 		unroll_array_from_complex_load(id, ptr, expr);
 		unroll_array_from_complex_load(id, ptr, expr);
 
 
-		auto &type = get<SPIRType>(result_type);
 		// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
 		// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
 		// when loading Image/Sampler descriptors. It does not hurt to check ID as well.
 		// when loading Image/Sampler descriptors. It does not hurt to check ID as well.
 		if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ptr, DecorationNonUniformEXT))
 		if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ptr, DecorationNonUniformEXT))
@@ -8714,13 +8759,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		                      (type.basetype == SPIRType::Struct || (type.columns > 1));
 		                      (type.basetype == SPIRType::Struct || (type.columns > 1));
 
 
 		SPIRExpression *e = nullptr;
 		SPIRExpression *e = nullptr;
-		if (!backend.array_is_value_type && !type.array.empty() && !forward)
+		if (!forward && expression_is_non_value_type_array(ptr))
 		{
 		{
 			// Complicated load case where we need to make a copy of ptr, but we cannot, because
 			// Complicated load case where we need to make a copy of ptr, but we cannot, because
 			// 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), ptr, StorageClassFunction, get_backing_variable_storage(ptr));
+			emit_array_copy(to_expression(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);
@@ -13385,7 +13430,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
 				if (ir.ids[block.return_value].get_type() != TypeUndef)
 				if (ir.ids[block.return_value].get_type() != TypeUndef)
 				{
 				{
 					emit_array_copy("SPIRV_Cross_return_value", block.return_value, StorageClassFunction,
 					emit_array_copy("SPIRV_Cross_return_value", block.return_value, StorageClassFunction,
-					                get_backing_variable_storage(block.return_value));
+					                get_expression_effective_storage_class(block.return_value));
 				}
 				}
 
 
 				if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) ||
 				if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) ||

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

@@ -467,6 +467,7 @@ protected:
 		bool supports_extensions = false;
 		bool supports_extensions = false;
 		bool supports_empty_struct = false;
 		bool supports_empty_struct = false;
 		bool array_is_value_type = true;
 		bool array_is_value_type = true;
+		bool buffer_offset_array_is_value_type = true;
 		bool comparison_image_samples_scalar = false;
 		bool comparison_image_samples_scalar = false;
 		bool native_pointers = false;
 		bool native_pointers = false;
 		bool support_small_type_sampling_result = false;
 		bool support_small_type_sampling_result = false;
@@ -585,7 +586,7 @@ protected:
 	SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id);
 	SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id);
 	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_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);
+	std::string to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset);
 	std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type);
 	std::string to_rerolled_array_expression(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);
@@ -762,6 +763,7 @@ protected:
 	void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
 	void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
 
 
 	bool expression_is_constant_null(uint32_t id) const;
 	bool expression_is_constant_null(uint32_t id) const;
+	bool expression_is_non_value_type_array(uint32_t ptr);
 	virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression);
 	virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression);
 
 
 	uint32_t get_integer_width_for_instruction(const Instruction &instr) const;
 	uint32_t get_integer_width_for_instruction(const Instruction &instr) const;

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

@@ -2946,24 +2946,37 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
 	if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us.
 	if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us.
 		coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components);
 		coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components);
 
 
-	if (hlsl_options.shader_model < 40 && lod)
+	if (hlsl_options.shader_model < 40)
 	{
 	{
 		string coord_filler;
 		string coord_filler;
-		for (uint32_t size = coord_components; size < 3; ++size)
+		uint32_t modifier_count = 0;
+
+		if (lod)
 		{
 		{
-			coord_filler += ", 0.0";
+			for (uint32_t size = coord_components; size < 3; ++size)
+				coord_filler += ", 0.0";
+			coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(lod) + ")";
+			modifier_count++;
 		}
 		}
-		coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(lod) + ")";
-	}
 
 
-	if (hlsl_options.shader_model < 40 && bias)
-	{
-		string coord_filler;
-		for (uint32_t size = coord_components; size < 3; ++size)
+		if (bias)
 		{
 		{
-			coord_filler += ", 0.0";
+			for (uint32_t size = coord_components; size < 3; ++size)
+				coord_filler += ", 0.0";
+			coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(bias) + ")";
+			modifier_count++;
 		}
 		}
-		coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_expression(bias) + ")";
+
+		if (proj)
+		{
+			for (uint32_t size = coord_components; size < 3; ++size)
+				coord_filler += ", 0.0";
+			coord_expr = "float4(" + coord_expr + coord_filler + ", " + to_extract_component_expression(coord, coord_components) + ")";
+			modifier_count++;
+		}
+
+		if (modifier_count > 1)
+			SPIRV_CROSS_THROW("Legacy HLSL can only use one of lod/bias/proj modifiers.");
 	}
 	}
 
 
 	if (op == OpImageFetch)
 	if (op == OpImageFetch)

+ 91 - 89
3rdparty/spirv-cross/spirv_msl.cpp

@@ -49,11 +49,20 @@ CompilerMSL::CompilerMSL(ParsedIR &&ir_)
 {
 {
 }
 }
 
 
+void CompilerMSL::add_msl_shader_input(const MSLShaderInput &si)
+{
+	inputs_by_location[si.location] = si;
+	if (si.builtin != BuiltInMax && !inputs_by_builtin.count(si.builtin))
+		inputs_by_builtin[si.builtin] = si;
+}
+
 void CompilerMSL::add_msl_vertex_attribute(const MSLVertexAttr &va)
 void CompilerMSL::add_msl_vertex_attribute(const MSLVertexAttr &va)
 {
 {
-	vtx_attrs_by_location[va.location] = va;
-	if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
-		vtx_attrs_by_builtin[va.builtin] = va;
+	MSLShaderInput si;
+	si.location = va.location;
+	si.format = va.format;
+	si.builtin = va.builtin;
+	add_msl_shader_input(si);
 }
 }
 
 
 void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
 void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
@@ -93,7 +102,12 @@ void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bo
 
 
 bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
 bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
 {
 {
-	return vtx_attrs_in_use.count(location) != 0;
+	return is_msl_shader_input_used(location);
+}
+
+bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
+{
+	return inputs_in_use.count(location) != 0;
 }
 }
 
 
 bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
 bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
@@ -1014,6 +1028,8 @@ string CompilerMSL::compile()
 	// 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;
 	backend.array_is_value_type = !msl_options.force_native_arrays;
 	backend.array_is_value_type = !msl_options.force_native_arrays;
+	// Arrays which are part of buffer objects are never considered to be native arrays.
+	backend.buffer_offset_array_is_value_type = false;
 
 
 	capture_output_to_buffer = msl_options.capture_output_to_buffer;
 	capture_output_to_buffer = msl_options.capture_output_to_buffer;
 	is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
 	is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
@@ -1458,11 +1474,11 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
 	}
 	}
 }
 }
 
 
-// If a vertex attribute exists at the location, it is marked as being used by this shader
+// If a shader input exists at the location, it is marked as being used by this shader
 void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
 void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
 {
 {
-	if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput))
-		vtx_attrs_in_use.insert(location);
+	if (storage == StorageClassInput)
+		inputs_in_use.insert(location);
 }
 }
 
 
 uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
 uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
@@ -1474,11 +1490,13 @@ uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t locat
 		return itr->second;
 		return itr->second;
 }
 }
 
 
-uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components)
+uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype)
 {
 {
 	uint32_t new_type_id = ir.increase_bound_by(1);
 	uint32_t new_type_id = ir.increase_bound_by(1);
 	auto &type = set<SPIRType>(new_type_id, get<SPIRType>(type_id));
 	auto &type = set<SPIRType>(new_type_id, get<SPIRType>(type_id));
 	type.vecsize = components;
 	type.vecsize = components;
+	if (basetype != SPIRType::Unknown)
+		type.basetype = basetype;
 	type.self = new_type_id;
 	type.self = new_type_id;
 	type.parent_type = type_id;
 	type.parent_type = type_id;
 	type.pointer = false;
 	type.pointer = false;
@@ -1634,11 +1652,9 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
 	if (get_decoration_bitset(var.self).get(DecorationLocation))
 	if (get_decoration_bitset(var.self).get(DecorationLocation))
 	{
 	{
 		uint32_t locn = get_decoration(var.self, DecorationLocation);
 		uint32_t locn = get_decoration(var.self, DecorationLocation);
-		if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+		if (storage == StorageClassInput)
 		{
 		{
-			type_id = ensure_correct_attribute_type(var.basetype, locn,
-			                                        location_meta ? location_meta->num_components : type.vecsize);
-
+			type_id = ensure_correct_input_type(var.basetype, locn, location_meta ? location_meta->num_components : 0);
 			if (!location_meta)
 			if (!location_meta)
 				var.basetype = type_id;
 				var.basetype = type_id;
 
 
@@ -1650,9 +1666,9 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		mark_location_as_used_by_shader(locn, storage);
 		mark_location_as_used_by_shader(locn, storage);
 	}
 	}
-	else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+	else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
 	{
 	{
-		uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+		uint32_t locn = inputs_by_builtin[builtin].location;
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		mark_location_as_used_by_shader(locn, storage);
 		mark_location_as_used_by_shader(locn, storage);
 	}
 	}
@@ -1797,19 +1813,18 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
 		if (get_decoration_bitset(var.self).get(DecorationLocation))
 		if (get_decoration_bitset(var.self).get(DecorationLocation))
 		{
 		{
 			uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
 			uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
-			if (storage == StorageClassInput &&
-			    (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+			if (storage == StorageClassInput)
 			{
 			{
-				var.basetype = ensure_correct_attribute_type(var.basetype, locn);
-				uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
+				var.basetype = ensure_correct_input_type(var.basetype, locn);
+				uint32_t mbr_type_id = ensure_correct_input_type(usable_type->self, locn);
 				ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 				ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 			}
 			}
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, storage);
 			mark_location_as_used_by_shader(locn, storage);
 		}
 		}
-		else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+		else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
 		{
 		{
-			uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
+			uint32_t locn = inputs_by_builtin[builtin].location + i;
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, storage);
 			mark_location_as_used_by_shader(locn, storage);
 		}
 		}
@@ -1987,9 +2002,9 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, storage);
 			mark_location_as_used_by_shader(locn, storage);
 		}
 		}
-		else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+		else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
 		{
 		{
-			uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
+			uint32_t locn = inputs_by_builtin[builtin].location + i;
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, storage);
 			mark_location_as_used_by_shader(locn, storage);
 		}
 		}
@@ -2114,9 +2129,9 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
 	if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
 	if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
 	{
 	{
 		uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation);
 		uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation);
-		if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+		if (storage == StorageClassInput)
 		{
 		{
-			mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
+			mbr_type_id = ensure_correct_input_type(mbr_type_id, locn);
 			var_type.member_types[mbr_idx] = mbr_type_id;
 			var_type.member_types[mbr_idx] = mbr_type_id;
 			ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 			ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 		}
 		}
@@ -2128,20 +2143,20 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
 		// The block itself might have a location and in this case, all members of the block
 		// The block itself might have a location and in this case, all members of the block
 		// receive incrementing locations.
 		// receive incrementing locations.
 		uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array);
 		uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array);
-		if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
+		if (storage == StorageClassInput)
 		{
 		{
-			mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
+			mbr_type_id = ensure_correct_input_type(mbr_type_id, locn);
 			var_type.member_types[mbr_idx] = mbr_type_id;
 			var_type.member_types[mbr_idx] = mbr_type_id;
 			ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 			ib_type.member_types[ib_mbr_idx] = mbr_type_id;
 		}
 		}
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		mark_location_as_used_by_shader(locn, storage);
 		mark_location_as_used_by_shader(locn, storage);
 	}
 	}
-	else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
+	else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
 	{
 	{
 		uint32_t locn = 0;
 		uint32_t locn = 0;
-		auto builtin_itr = vtx_attrs_by_builtin.find(builtin);
-		if (builtin_itr != end(vtx_attrs_by_builtin))
+		auto builtin_itr = inputs_by_builtin.find(builtin);
+		if (builtin_itr != end(inputs_by_builtin))
 			locn = builtin_itr->second.location;
 			locn = builtin_itr->second.location;
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 		mark_location_as_used_by_shader(locn, storage);
 		mark_location_as_used_by_shader(locn, storage);
@@ -2222,9 +2237,9 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 				set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 				set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 				mark_location_as_used_by_shader(locn, StorageClassInput);
 				mark_location_as_used_by_shader(locn, StorageClassInput);
 			}
 			}
-			else if (vtx_attrs_by_builtin.count(builtin))
+			else if (inputs_by_builtin.count(builtin))
 			{
 			{
-				uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+				uint32_t locn = inputs_by_builtin[builtin].location;
 				set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 				set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 				mark_location_as_used_by_shader(locn, StorageClassInput);
 				mark_location_as_used_by_shader(locn, StorageClassInput);
 			}
 			}
@@ -2283,9 +2298,9 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, StorageClassInput);
 			mark_location_as_used_by_shader(locn, StorageClassInput);
 		}
 		}
-		else if (vtx_attrs_by_builtin.count(builtin))
+		else if (inputs_by_builtin.count(builtin))
 		{
 		{
-			uint32_t locn = vtx_attrs_by_builtin[builtin].location;
+			uint32_t locn = inputs_by_builtin[builtin].location;
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
 			mark_location_as_used_by_shader(locn, StorageClassInput);
 			mark_location_as_used_by_shader(locn, StorageClassInput);
 		}
 		}
@@ -2488,8 +2503,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
 		// accept them. We can't put them in the struct at all, or otherwise the compiler
 		// accept them. We can't put them in the struct at all, or otherwise the compiler
 		// complains that the outputs weren't explicitly marked.
 		// complains that the outputs weren't explicitly marked.
 		if (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput && !patch &&
 		if (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput && !patch &&
-			((is_builtin && ((bi_type == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) ||
-		                    (bi_type == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))) ||
+		    ((is_builtin && ((bi_type == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) ||
+		                     (bi_type == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))) ||
 		     (!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location)))))
 		     (!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location)))))
 		{
 		{
 			hidden = true;
 			hidden = true;
@@ -2784,63 +2799,49 @@ uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn buil
 	return type_id;
 	return type_id;
 }
 }
 
 
-// Ensure that the type is compatible with the vertex attribute.
+// Ensure that the type is compatible with the shader input.
 // If it is, simply return the given type ID.
 // If it is, simply return the given type ID.
 // Otherwise, create a new type, and return its ID.
 // Otherwise, create a new type, and return its ID.
-uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components)
+uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components)
 {
 {
 	auto &type = get<SPIRType>(type_id);
 	auto &type = get<SPIRType>(type_id);
 
 
-	auto p_va = vtx_attrs_by_location.find(location);
-	if (p_va == end(vtx_attrs_by_location))
+	auto p_va = inputs_by_location.find(location);
+	if (p_va == end(inputs_by_location))
 	{
 	{
-		if (num_components != 0 && type.vecsize != num_components)
+		if (num_components > type.vecsize)
 			return build_extended_vector_type(type_id, num_components);
 			return build_extended_vector_type(type_id, num_components);
 		else
 		else
 			return type_id;
 			return type_id;
 	}
 	}
 
 
+	if (num_components == 0)
+		num_components = p_va->second.vecsize;
+
 	switch (p_va->second.format)
 	switch (p_va->second.format)
 	{
 	{
-	case MSL_VERTEX_FORMAT_UINT8:
+	case MSL_SHADER_INPUT_FORMAT_UINT8:
 	{
 	{
 		switch (type.basetype)
 		switch (type.basetype)
 		{
 		{
 		case SPIRType::UByte:
 		case SPIRType::UByte:
 		case SPIRType::UShort:
 		case SPIRType::UShort:
 		case SPIRType::UInt:
 		case SPIRType::UInt:
-			if (num_components != 0 && type.vecsize != num_components)
+			if (num_components > type.vecsize)
 				return build_extended_vector_type(type_id, num_components);
 				return build_extended_vector_type(type_id, num_components);
 			else
 			else
 				return type_id;
 				return type_id;
 
 
 		case SPIRType::Short:
 		case SPIRType::Short:
+			return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
+			                                  SPIRType::UShort);
 		case SPIRType::Int:
 		case SPIRType::Int:
-			break;
+			return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
+			                                  SPIRType::UInt);
 
 
 		default:
 		default:
 			SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
 			SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
 		}
 		}
-
-		uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
-		uint32_t base_type_id = next_id++;
-		auto &base_type = set<SPIRType>(base_type_id);
-		base_type = type;
-		base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt;
-		base_type.pointer = false;
-		if (num_components != 0)
-			base_type.vecsize = num_components;
-
-		if (!type.pointer)
-			return base_type_id;
-
-		uint32_t ptr_type_id = next_id++;
-		auto &ptr_type = set<SPIRType>(ptr_type_id);
-		ptr_type = base_type;
-		ptr_type.pointer = true;
-		ptr_type.storage = type.storage;
-		ptr_type.parent_type = base_type_id;
-		return ptr_type_id;
 	}
 	}
 
 
 	case MSL_VERTEX_FORMAT_UINT16:
 	case MSL_VERTEX_FORMAT_UINT16:
@@ -2849,41 +2850,22 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
 		{
 		{
 		case SPIRType::UShort:
 		case SPIRType::UShort:
 		case SPIRType::UInt:
 		case SPIRType::UInt:
-			if (num_components != 0 && type.vecsize != num_components)
+			if (num_components > type.vecsize)
 				return build_extended_vector_type(type_id, num_components);
 				return build_extended_vector_type(type_id, num_components);
 			else
 			else
 				return type_id;
 				return type_id;
 
 
 		case SPIRType::Int:
 		case SPIRType::Int:
-			break;
+			return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
+			                                  SPIRType::UInt);
 
 
 		default:
 		default:
 			SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
 			SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
 		}
 		}
-
-		uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
-		uint32_t base_type_id = next_id++;
-		auto &base_type = set<SPIRType>(base_type_id);
-		base_type = type;
-		base_type.basetype = SPIRType::UInt;
-		base_type.pointer = false;
-		if (num_components != 0)
-			base_type.vecsize = num_components;
-
-		if (!type.pointer)
-			return base_type_id;
-
-		uint32_t ptr_type_id = next_id++;
-		auto &ptr_type = set<SPIRType>(ptr_type_id);
-		ptr_type = base_type;
-		ptr_type.pointer = true;
-		ptr_type.storage = type.storage;
-		ptr_type.parent_type = base_type_id;
-		return ptr_type_id;
 	}
 	}
 
 
 	default:
 	default:
-		if (num_components != 0 && type.vecsize != num_components)
+		if (num_components > type.vecsize)
 			type_id = build_extended_vector_type(type_id, num_components);
 			type_id = build_extended_vector_type(type_id, num_components);
 		break;
 		break;
 	}
 	}
@@ -3840,17 +3822,21 @@ void CompilerMSL::emit_custom_functions()
 			static const char *function_name_tags[] = {
 			static const char *function_name_tags[] = {
 				"FromConstantToStack",    "FromConstantToThreadGroup", "FromStackToStack",
 				"FromConstantToStack",    "FromConstantToThreadGroup", "FromStackToStack",
 				"FromStackToThreadGroup", "FromThreadGroupToStack",    "FromThreadGroupToThreadGroup",
 				"FromStackToThreadGroup", "FromThreadGroupToStack",    "FromThreadGroupToThreadGroup",
+				"FromDeviceToDevice", "FromConstantToDevice", "FromStackToDevice",
+				"FromThreadGroupToDevice", "FromDeviceToStack", "FromDeviceToThreadGroup",
 			};
 			};
 
 
 			static const char *src_address_space[] = {
 			static const char *src_address_space[] = {
 				"constant", "constant", "thread const", "thread const", "threadgroup const", "threadgroup const",
 				"constant", "constant", "thread const", "thread const", "threadgroup const", "threadgroup const",
+				"device const", "constant", "thread const", "threadgroup const", "device const", "device const",
 			};
 			};
 
 
 			static const char *dst_address_space[] = {
 			static const char *dst_address_space[] = {
 				"thread", "threadgroup", "thread", "threadgroup", "thread", "threadgroup",
 				"thread", "threadgroup", "thread", "threadgroup", "thread", "threadgroup",
+				"device", "device", "device", "device", "thread", "threadgroup",
 			};
 			};
 
 
-			for (uint32_t variant = 0; variant < 6; variant++)
+			for (uint32_t variant = 0; variant < 12; variant++)
 			{
 			{
 				uint32_t dimensions = spv_func - SPVFuncImplArrayCopyMultidimBase;
 				uint32_t dimensions = spv_func - SPVFuncImplArrayCopyMultidimBase;
 				string tmp = "template<typename T";
 				string tmp = "template<typename T";
@@ -6889,6 +6875,10 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
 		{
 		{
 			is_constant = true;
 			is_constant = true;
 		}
 		}
+		else if (rhs_storage == StorageClassUniform)
+		{
+			is_constant = true;
+		}
 
 
 		// For the case where we have OpLoad triggering an array copy,
 		// For the case where we have OpLoad triggering an array copy,
 		// we cannot easily detect this case ahead of time since it's
 		// we cannot easily detect this case ahead of time since it's
@@ -6917,6 +6907,18 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
 			tag = "FromThreadGroupToStack";
 			tag = "FromThreadGroupToStack";
 		else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup)
 		else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup)
 			tag = "FromThreadGroupToThreadGroup";
 			tag = "FromThreadGroupToThreadGroup";
+		else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassStorageBuffer)
+			tag = "FromDeviceToDevice";
+		else if (lhs_storage == StorageClassStorageBuffer && is_constant)
+			tag = "FromConstantToDevice";
+		else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassWorkgroup)
+			tag = "FromThreadGroupToDevice";
+		else if (lhs_storage == StorageClassStorageBuffer && rhs_thread)
+			tag = "FromStackToDevice";
+		else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassStorageBuffer)
+			tag = "FromDeviceToThreadGroup";
+		else if (lhs_thread && rhs_storage == StorageClassStorageBuffer)
+			tag = "FromDeviceToStack";
 		else
 		else
 			SPIRV_CROSS_THROW("Unknown storage class used for copying arrays.");
 			SPIRV_CROSS_THROW("Unknown storage class used for copying arrays.");
 
 
@@ -6963,8 +6965,8 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
 	if (p_v_lhs)
 	if (p_v_lhs)
 		flush_variable_declaration(p_v_lhs->self);
 		flush_variable_declaration(p_v_lhs->self);
 
 
-	emit_array_copy(to_expression(id_lhs), id_rhs, get_backing_variable_storage(id_lhs),
-	                get_backing_variable_storage(id_rhs));
+	emit_array_copy(to_expression(id_lhs), id_rhs, get_expression_effective_storage_class(id_lhs),
+	                get_expression_effective_storage_class(id_rhs));
 	register_write(id_lhs);
 	register_write(id_lhs);
 
 
 	return true;
 	return true;

+ 43 - 14
3rdparty/spirv-cross/spirv_msl.hpp

@@ -27,24 +27,43 @@
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
 
 
-// Indicates the format of the vertex attribute. Currently limited to specifying
-// if the attribute is an 8-bit unsigned integer, 16-bit unsigned integer, or
+// Indicates the format of a shader input. Currently limited to specifying
+// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or
 // some other format.
 // some other format.
-enum MSLVertexFormat
+enum MSLShaderInputFormat
 {
 {
-	MSL_VERTEX_FORMAT_OTHER = 0,
-	MSL_VERTEX_FORMAT_UINT8 = 1,
-	MSL_VERTEX_FORMAT_UINT16 = 2,
-	MSL_VERTEX_FORMAT_INT_MAX = 0x7fffffff
+	MSL_SHADER_INPUT_FORMAT_OTHER = 0,
+	MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
+	MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
+
+	// Deprecated aliases.
+	MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_INPUT_FORMAT_OTHER,
+	MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_INPUT_FORMAT_UINT8,
+	MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_INPUT_FORMAT_UINT16,
+
+	MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff
 };
 };
+typedef SPIRV_CROSS_DEPRECATED("Use MSLShaderInputFormat.") MSLShaderInputFormat MSLVertexFormat;
 
 
 // Defines MSL characteristics of a vertex attribute at a particular location.
 // Defines MSL characteristics of a vertex attribute at a particular location.
 // After compilation, it is possible to query whether or not this location was used.
 // After compilation, it is possible to query whether or not this location was used.
-struct MSLVertexAttr
+struct SPIRV_CROSS_DEPRECATED("Use MSLShaderInput.") MSLVertexAttr
+{
+	uint32_t location = 0;
+	MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
+	spv::BuiltIn builtin = spv::BuiltInMax;
+};
+
+// Defines MSL characteristics of an input variable at a particular location.
+// After compilation, it is possible to query whether or not this location was used.
+// If vecsize is nonzero, it must be greater than or equal to the vecsize declared in the shader,
+// or behavior is undefined.
+struct MSLShaderInput
 {
 {
 	uint32_t location = 0;
 	uint32_t location = 0;
-	MSLVertexFormat format = MSL_VERTEX_FORMAT_OTHER;
+	MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
 	spv::BuiltIn builtin = spv::BuiltInMax;
 	spv::BuiltIn builtin = spv::BuiltInMax;
+	uint32_t vecsize = 0;
 };
 };
 
 
 // Matches the binding index of a MSL resource for a binding within a descriptor set.
 // Matches the binding index of a MSL resource for a binding within a descriptor set.
@@ -423,8 +442,14 @@ public:
 	// vertex content locations to MSL attributes. If vertex attributes are provided,
 	// vertex content locations to MSL attributes. If vertex attributes are provided,
 	// is_msl_vertex_attribute_used() will return true after calling ::compile() if
 	// is_msl_vertex_attribute_used() will return true after calling ::compile() if
 	// the location was used by the MSL code.
 	// the location was used by the MSL code.
+	SPIRV_CROSS_DEPRECATED("Use add_msl_shader_input().")
 	void add_msl_vertex_attribute(const MSLVertexAttr &attr);
 	void add_msl_vertex_attribute(const MSLVertexAttr &attr);
 
 
+	// input is a shader input description used to fix up shader input variables.
+	// If shader inputs are provided, is_msl_shader_input_used() will return true after
+	// calling ::compile() if the location was used by the MSL code.
+	void add_msl_shader_input(const MSLShaderInput &attr);
+
 	// resource is a resource binding to indicate the MSL buffer,
 	// resource is a resource binding to indicate the MSL buffer,
 	// texture or sampler index to use for a particular SPIR-V description set
 	// texture or sampler index to use for a particular SPIR-V description set
 	// and binding. If resource bindings are provided,
 	// and binding. If resource bindings are provided,
@@ -456,8 +481,12 @@ public:
 	void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage);
 	void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage);
 
 
 	// Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
 	// Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
+	SPIRV_CROSS_DEPRECATED("Use is_msl_shader_input_used().")
 	bool is_msl_vertex_attribute_used(uint32_t location);
 	bool is_msl_vertex_attribute_used(uint32_t location);
 
 
+	// Query after compilation is done. This allows you to check if an input location was used by the shader.
+	bool is_msl_shader_input_used(uint32_t location);
+
 	// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
 	// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
 	// Constexpr samplers are always assumed to be emitted.
 	// Constexpr samplers are always assumed to be emitted.
 	// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
 	// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
@@ -686,7 +715,7 @@ protected:
 
 
 	void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
 	void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
 	uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
 	uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
-	uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
+	uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
 
 
 	void emit_custom_templates();
 	void emit_custom_templates();
 	void emit_custom_functions();
 	void emit_custom_functions();
@@ -797,9 +826,9 @@ protected:
 
 
 	Options msl_options;
 	Options msl_options;
 	std::set<SPVFuncImpl> spv_function_implementations;
 	std::set<SPVFuncImpl> spv_function_implementations;
-	std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
-	std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_builtin;
-	std::unordered_set<uint32_t> vtx_attrs_in_use;
+	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_location;
+	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
+	std::unordered_set<uint32_t> inputs_in_use;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> typedef_lines;
 	std::set<std::string> typedef_lines;
@@ -881,7 +910,7 @@ protected:
 	bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
 	bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
 
 
 	uint32_t get_target_components_for_fragment_location(uint32_t location) const;
 	uint32_t get_target_components_for_fragment_location(uint32_t location) const;
-	uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components);
+	uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype = SPIRType::Unknown);
 
 
 	bool suppress_missing_prototypes = false;
 	bool suppress_missing_prototypes = false;