Merge pull request #1791 from billhollings/msl-out-of-order-struct-offsets

MSL: Fix type redirection when struct members are reordered to align with offsets.
diff --git a/reference/opt/shaders-msl/vert/uniform-struct-out-of-order-offests.vert b/reference/opt/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
new file mode 100644
index 0000000..c69775e
--- /dev/null
+++ b/reference/opt/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
@@ -0,0 +1,32 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct data_u_t
+{
+    int4 m1[3];
+    uint m3;
+    uint3 m2;
+    int4 m0[8];
+};
+
+struct main0_out
+{
+    float foo [[user(locn0)]];
+    float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+    float4 vtx_posn [[attribute(0)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]], constant data_u_t& data_u [[buffer(0)]])
+{
+    main0_out out = {};
+    out.gl_Position = in.vtx_posn;
+    out.foo = float((uint3(data_u.m1[1].xyz) + data_u.m2).y * uint(data_u.m0[4].x));
+    return out;
+}
+
diff --git a/reference/shaders-msl/vert/uniform-struct-out-of-order-offests.vert b/reference/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
new file mode 100644
index 0000000..4f71b20
--- /dev/null
+++ b/reference/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
@@ -0,0 +1,35 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct data_u_t
+{
+    int4 m1[3];
+    uint m3;
+    uint3 m2;
+    int4 m0[8];
+};
+
+struct main0_out
+{
+    float foo [[user(locn0)]];
+    float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+    float4 vtx_posn [[attribute(0)]];
+};
+
+vertex main0_out main0(main0_in in [[stage_in]], constant data_u_t& data_u [[buffer(0)]])
+{
+    main0_out out = {};
+    out.gl_Position = in.vtx_posn;
+    int4 a = data_u.m1[1];
+    uint3 b = data_u.m2;
+    int c = data_u.m0[4].x;
+    out.foo = float((uint3(a.xyz) + b).y * uint(c));
+    return out;
+}
+
diff --git a/shaders-msl/vert/uniform-struct-out-of-order-offests.vert b/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
new file mode 100644
index 0000000..21234f9
--- /dev/null
+++ b/shaders-msl/vert/uniform-struct-out-of-order-offests.vert
@@ -0,0 +1,31 @@
+#version 450
+
+out gl_PerVertex
+{
+    vec4 gl_Position;
+    float gl_PointSize;
+    float gl_ClipDistance[1];
+    float gl_CullDistance[1];
+};
+
+layout(set = 0, binding = 0, std140) uniform data_u_t
+{
+    layout(offset = 80) mediump int m0[8];
+    layout(offset = 0) mediump ivec4 m1[3];
+    layout(offset = 64) uvec3 m2;
+    layout(offset = 48) mediump uint m3;
+} data_u;
+
+layout(location = 0) in vec4 vtx_posn;
+layout(location = 0) out mediump float foo;
+
+void main()
+{
+    gl_Position = vtx_posn;
+    ivec4 a = data_u.m1[1];
+    uvec3 b = data_u.m2;
+    int c = data_u.m0[4];
+    foo = (a.xyz + b).y * c;
+}
+
+
diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp
index 0a00947..61acda6 100644
--- a/spirv_glsl.cpp
+++ b/spirv_glsl.cpp
@@ -8761,13 +8761,16 @@
 
 			access_chain_is_arrayed = true;
 		}
-		// For structs, the index refers to a constant, which indexes into the members.
+		// For structs, the index refers to a constant, which indexes into the members, possibly through a redirection mapping.
 		// We also check if this member is a builtin, since we then replace the entire expression with the builtin one.
 		else if (type->basetype == SPIRType::Struct)
 		{
 			if (!is_literal)
 				index = evaluate_constant_u32(index);
 
+			if (index < uint32_t(type->member_type_index_redirection.size()))
+				index = type->member_type_index_redirection[index];
+
 			if (index >= type->member_types.size())
 				SPIRV_CROSS_THROW("Member index is out of bounds!");
 
diff --git a/spirv_msl.cpp b/spirv_msl.cpp
index ceed1c6..a7ec6d9 100644
--- a/spirv_msl.cpp
+++ b/spirv_msl.cpp
@@ -13234,9 +13234,6 @@
 
 string CompilerMSL::to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain)
 {
-	if (index < uint32_t(type.member_type_index_redirection.size()))
-		index = type.member_type_index_redirection[index];
-
 	auto *var = maybe_get<SPIRVariable>(base);
 	// If this is a buffer array, we have to dereference the buffer pointers.
 	// Otherwise, if this is a pointer expression, dereference it.
@@ -15374,11 +15371,13 @@
 		meta.members[mbr_idx] = mbr_meta_cpy[mbr_idxs[mbr_idx]];
 	}
 
+	// If we're sorting by Offset, this might affect user code which accesses a buffer block.
+	// We will need to redirect member indices from defined index to sorted index using reverse lookup.
 	if (sort_aspect == SortAspect::Offset)
 	{
-		// If we're sorting by Offset, this might affect user code which accesses a buffer block.
-		// We will need to redirect member indices from one index to sorted index.
-		type.member_type_index_redirection = std::move(mbr_idxs);
+		type.member_type_index_redirection.resize(mbr_cnt);
+		for (uint32_t map_idx = 0; map_idx < mbr_cnt; map_idx++)
+			type.member_type_index_redirection[mbr_idxs[map_idx]] = map_idx;
 	}
 }