diff --git a/reference/shaders-msl/flatten/rowmajor.flatten.vert b/reference/shaders-msl/flatten/rowmajor.flatten.vert index b5df8b06..688e66e3 100644 --- a/reference/shaders-msl/flatten/rowmajor.flatten.vert +++ b/reference/shaders-msl/flatten/rowmajor.flatten.vert @@ -1,5 +1,3 @@ -#pragma clang diagnostic ignored "-Wmissing-prototypes" - #include #include @@ -9,7 +7,7 @@ struct UBO { float4x4 uMVPR; float4x4 uMVPC; - float2x4 uMVP; + float4x4 uMVP; }; struct main0_out @@ -22,16 +20,10 @@ struct main0_in float4 aVertex [[attribute(0)]]; }; -// Implementation of a conversion of matrix content from RowMajor to ColumnMajor organization. -float2x4 spvConvertFromRowMajor2x4(float2x4 m) -{ - return float2x4(float4(m[0][0], m[0][2], m[1][0], m[1][2]), float4(m[0][1], m[0][3], m[1][1], m[1][3])); -} - vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) { main0_out out = {}; - float2 v = in.aVertex * spvConvertFromRowMajor2x4(_18.uMVP); + float2 v = in.aVertex * transpose(float2x4(_18.uMVP[0].xy, _18.uMVP[1].xy, _18.uMVP[2].xy, _18.uMVP[3].xy)); out.gl_Position = (_18.uMVPR * in.aVertex) + (in.aVertex * _18.uMVPC); return out; } diff --git a/reference/shaders-msl/flatten/struct.flatten.vert b/reference/shaders-msl/flatten/struct.flatten.vert index 954f9255..38199d7b 100644 --- a/reference/shaders-msl/flatten/struct.flatten.vert +++ b/reference/shaders-msl/flatten/struct.flatten.vert @@ -33,7 +33,7 @@ vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]] main0_out out = {}; out.gl_Position = _18.uMVP * in.aVertex; out.vColor = float4(0.0); - float3 L = in.aVertex.xyz - float3(_18.light.Position); + float3 L = in.aVertex.xyz - _18.light.Position; out.vColor += ((_18.light.Color * fast::clamp(1.0 - (length(L) / _18.light.Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(L))); return out; } diff --git a/spirv_common.hpp b/spirv_common.hpp index a0ddac7a..39e7deba 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -1416,6 +1416,7 @@ enum ExtendedDecorations // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends. // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing // is converting float3 to packed_float3 for example. + // If this is marked on a struct, it means the struct itself must use only Packed types for all its members. SPIRVCrossDecorationPhysicalTypePacked, // The padding in bytes before declaring this struct member. diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 36d1f318..02edde1b 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -2392,6 +2392,48 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l return type_id; } +void CompilerMSL::mark_scalar_layout_structs(const SPIRType &type) +{ + uint32_t mbr_cnt = type.member_types.size(); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + auto &mbr_type = get(type.member_types[i]); + if (mbr_type.basetype == SPIRType::Struct) + { + if (has_extended_decoration(mbr_type.self, SPIRVCrossDecorationPhysicalTypePacked)) + continue; + + uint32_t msl_alignment = get_declared_struct_member_alignment_msl(type, i); + uint32_t msl_size = get_declared_struct_member_size_msl(type, i); + uint32_t spirv_offset = type_struct_member_offset(type, i); + uint32_t spirv_offset_next; + if (i + 1 < mbr_cnt) + spirv_offset_next = type_struct_member_offset(type, i + 1); + else + spirv_offset_next = spirv_offset + msl_size; + + // Both are complicated cases. In scalar layout, a struct of float3 might just consume 12 bytes, + // and the next member will be placed at offset 12. + bool struct_is_misaligned = (spirv_offset % msl_alignment) != 0; + bool struct_is_too_large = spirv_offset + msl_size > spirv_offset_next; + + if (struct_is_misaligned || struct_is_too_large) + { + set_extended_decoration(mbr_type.self, SPIRVCrossDecorationPhysicalTypePacked); + + // Problem case! Struct needs to be placed at an awkward alignment. + // Mark every member of the child struct as packed. + uint32_t child_mbr_cnt = mbr_type.member_types.size(); + for (uint32_t j = 0; j < child_mbr_cnt; j++) + set_extended_member_decoration(mbr_type.self, j, SPIRVCrossDecorationPhysicalTypePacked); + } + + // Traverse ... + mark_scalar_layout_structs(mbr_type); + } + } +} + // Sort the members of the struct type by offset, and pack and then pad members where needed // to align MSL members with SPIR-V offsets. The struct members are iterated twice. Packing // occurs first, followed by padding, because packing a member reduces both its size and its @@ -2550,7 +2592,9 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in uint32_t elems_per_stride = array_stride / (mbr_type.width / 8); - if (elems_per_stride > 4) + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4) SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); auto physical_type = mbr_type; @@ -2562,7 +2606,9 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in set_decoration(type_id, DecorationArrayStride, array_stride); // Remove packed_ for vectors of size 1, 2 and 4. - if (elems_per_stride != 3) + if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) + SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix scalar and std140 layout rules."); + else unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); } else if (is_matrix(mbr_type)) @@ -2572,7 +2618,9 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in uint32_t elems_per_stride = matrix_stride / (mbr_type.width / 8); - if (elems_per_stride > 4) + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4) SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); @@ -2588,7 +2636,9 @@ void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t in set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); // Remove packed_ for vectors of size 1, 2 and 4. - if (elems_per_stride != 3) + if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) + SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix scalar and std140 layout rules."); + else unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); } @@ -3566,6 +3616,16 @@ void CompilerMSL::emit_specialization_constants_and_structs() unordered_set declared_structs; unordered_set aligned_structs; + // First, we need to deal with scalar block layout. + // It is possible that a struct may have to be placed at an alignment which does not match the innate alignment of the struct itself. + // In that case, if such a case exists for a struct, we must force that all elements of the struct become packed_ types. + // This makes the struct alignment as small as physically possible. + // When we actually align the struct later, we can insert padding as necessary to make the packed members behave like normally aligned types. + ir.for_each_typed_id([&](uint32_t type_id, const SPIRType &type) { + if (type.basetype == SPIRType::Struct && has_extended_decoration(type_id, SPIRVCrossDecorationBufferBlockRepacked)) + mark_scalar_layout_structs(type); + }); + // Very particular use of the soft loop lock. // align_struct may need to create custom types on the fly, but we don't care about // these types for purpose of iterating over them in ir.ids_for_type and friends. diff --git a/spirv_msl.hpp b/spirv_msl.hpp index ff6cd0f2..05e4b31e 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -535,6 +535,7 @@ protected: std::string to_component_argument(uint32_t id); void align_struct(SPIRType &ib_type, std::unordered_set &aligned_structs); + void mark_scalar_layout_structs(const SPIRType &ib_type); void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index); bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const; std::string get_argument_address_space(const SPIRVariable &argument);