diff --git a/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 00000000..1528c835 --- /dev/null +++ b/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.pos; + out.gl_ClipDistance[0] = in.pos.x; + out.gl_ClipDistance[1] = in.pos.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert b/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert index a414c985..2d989290 100644 --- a/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert +++ b/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert @@ -7,6 +7,8 @@ struct main0_out { float4 gl_Position [[position]]; float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; }; vertex main0_out main0() @@ -15,6 +17,8 @@ vertex main0_out main0() out.gl_Position = float4(10.0); out.gl_ClipDistance[0] = 1.0; out.gl_ClipDistance[1] = 4.0; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; return out; } diff --git a/reference/opt/shaders-msl/frag/clip-distance-varying.frag b/reference/opt/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 00000000..9a72d5ba --- /dev/null +++ b/reference/opt/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray gl_ClipDistance = {}; + gl_ClipDistance[0] = in.gl_ClipDistance_0; + gl_ClipDistance[1] = in.gl_ClipDistance_1; + out.FragColor = float4((1.0 - gl_ClipDistance[0]) - gl_ClipDistance[1]); + return out; +} + diff --git a/reference/opt/shaders-msl/vert/clip-distance-block.vert b/reference/opt/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 00000000..af58f35f --- /dev/null +++ b/reference/opt/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese b/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese index d8921bb8..738b073a 100644 --- a/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese +++ b/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese @@ -247,6 +247,7 @@ struct main0_out float4 out_var_TEXCOORD10_centroid [[user(locn2)]]; float4 out_var_TEXCOORD11_centroid [[user(locn3)]]; float gl_ClipDistance [[clip_distance]] [1]; + float gl_ClipDistance_0 [[user(clip0)]]; float4 gl_Position [[position]]; }; @@ -410,6 +411,7 @@ struct main0_patchIn out.out_var_TEXCOORD10_centroid = float4(_256.x, _256.y, _256.z, _118.w); out.out_var_TEXCOORD11_centroid = _259; out.gl_ClipDistance[0u] = dot(View.View_GlobalClippingPlane, float4(_565.xyz - float3(View.View_PreViewTranslation), 1.0)); + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; return out; } diff --git a/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 00000000..ae42798e --- /dev/null +++ b/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct VSOut +{ + float4 pos; + float2 clip; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +VSOut _main(thread const float4& pos) +{ + VSOut vout; + vout.pos = pos; + vout.clip = pos.xy; + return vout; +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 pos = in.pos; + float4 param = pos; + VSOut flattenTemp = _main(param); + out.gl_Position = flattenTemp.pos; + out.gl_ClipDistance[0] = flattenTemp.clip.x; + out.gl_ClipDistance[1] = flattenTemp.clip.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert b/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert index a414c985..2d989290 100644 --- a/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert +++ b/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert @@ -7,6 +7,8 @@ struct main0_out { float4 gl_Position [[position]]; float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; }; vertex main0_out main0() @@ -15,6 +17,8 @@ vertex main0_out main0() out.gl_Position = float4(10.0); out.gl_ClipDistance[0] = 1.0; out.gl_ClipDistance[1] = 4.0; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; return out; } diff --git a/reference/shaders-msl/frag/clip-distance-varying.frag b/reference/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 00000000..9a72d5ba --- /dev/null +++ b/reference/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray gl_ClipDistance = {}; + gl_ClipDistance[0] = in.gl_ClipDistance_0; + gl_ClipDistance[1] = in.gl_ClipDistance_1; + out.FragColor = float4((1.0 - gl_ClipDistance[0]) - gl_ClipDistance[1]); + return out; +} + diff --git a/reference/shaders-msl/vert/clip-distance-block.vert b/reference/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 00000000..af58f35f --- /dev/null +++ b/reference/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese b/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese index d8921bb8..738b073a 100644 --- a/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese +++ b/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese @@ -247,6 +247,7 @@ struct main0_out float4 out_var_TEXCOORD10_centroid [[user(locn2)]]; float4 out_var_TEXCOORD11_centroid [[user(locn3)]]; float gl_ClipDistance [[clip_distance]] [1]; + float gl_ClipDistance_0 [[user(clip0)]]; float4 gl_Position [[position]]; }; @@ -410,6 +411,7 @@ struct main0_patchIn out.out_var_TEXCOORD10_centroid = float4(_256.x, _256.y, _256.z, _118.w); out.out_var_TEXCOORD11_centroid = _259; out.gl_ClipDistance[0u] = dot(View.View_GlobalClippingPlane, float4(_565.xyz - float3(View.View_PreViewTranslation), 1.0)); + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; return out; } diff --git a/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 00000000..59ec3f91 --- /dev/null +++ b/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,91 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 56 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %pos_1 %_entryPointOutput_pos %_entryPointOutput_clip + OpSource HLSL 500 + OpName %main "main" + OpName %VSOut "VSOut" + OpMemberName %VSOut 0 "pos" + OpMemberName %VSOut 1 "clip" + OpName %_main_vf4_ "@main(vf4;" + OpName %pos "pos" + OpName %vout "vout" + OpName %pos_0 "pos" + OpName %pos_1 "pos" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %_entryPointOutput_clip "@entryPointOutput.clip" + OpDecorate %pos_1 Location 0 + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput_clip BuiltIn ClipDistance + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %v2float = OpTypeVector %float 2 + %VSOut = OpTypeStruct %v4float %v2float + %11 = OpTypeFunction %VSOut %_ptr_Function_v4float +%_ptr_Function_VSOut = OpTypePointer Function %VSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %pos_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_pos = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_entryPointOutput_clip = OpVariable %_ptr_Output__arr_float_uint_2 Output + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %pos_0 = OpVariable %_ptr_Function_v4float Function +%flattenTemp = OpVariable %_ptr_Function_VSOut Function + %param = OpVariable %_ptr_Function_v4float Function + %32 = OpLoad %v4float %pos_1 + OpStore %pos_0 %32 + %35 = OpLoad %v4float %pos_0 + OpStore %param %35 + %36 = OpFunctionCall %VSOut %_main_vf4_ %param + OpStore %flattenTemp %36 + %39 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %40 = OpLoad %v4float %39 + OpStore %_entryPointOutput_pos %40 + %48 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_0 + %49 = OpLoad %float %48 + %51 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_0 + OpStore %51 %49 + %53 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_1 + %54 = OpLoad %float %53 + %55 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_1 + OpStore %55 %54 + OpReturn + OpFunctionEnd + %_main_vf4_ = OpFunction %VSOut None %11 + %pos = OpFunctionParameter %_ptr_Function_v4float + %14 = OpLabel + %vout = OpVariable %_ptr_Function_VSOut Function + %19 = OpLoad %v4float %pos + %20 = OpAccessChain %_ptr_Function_v4float %vout %int_0 + OpStore %20 %19 + %22 = OpLoad %v4float %pos + %23 = OpVectorShuffle %v2float %22 %22 0 1 + %25 = OpAccessChain %_ptr_Function_v2float %vout %int_1 + OpStore %25 %23 + %26 = OpLoad %VSOut %vout + OpReturnValue %26 + OpFunctionEnd diff --git a/shaders-msl/frag/clip-distance-varying.frag b/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 00000000..df49bd51 --- /dev/null +++ b/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,10 @@ +#version 450 + +in float gl_ClipDistance[2]; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0 - gl_ClipDistance[0] - gl_ClipDistance[1]); +} diff --git a/shaders-msl/vert/clip-distance-block.vert b/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 00000000..93ed3115 --- /dev/null +++ b/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec4 Position; +out gl_PerVertex +{ + vec4 gl_Position; + float gl_ClipDistance[2]; +}; + +void main() +{ + gl_Position = Position; + gl_ClipDistance[0] = Position.x; + gl_ClipDistance[1] = Position.y; +} diff --git a/spirv_msl.cpp b/spirv_msl.cpp index c0db4f2f..46f55f6a 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -1611,9 +1611,23 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage if (is_builtin) set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); - // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. - if (!strip_array) + bool flatten_from_ib_var = false; + + if (storage == StorageClassOutput && builtin == BuiltInClipDistance) { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(get_variable_data_type_id(var)); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput)); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + } + else if (!strip_array) + { + // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. entry_func.add_local_variable(var.self); // We need to declare the variable early and at entry-point scope. vars_needing_early_declaration.push_back(var.self); @@ -1668,6 +1682,12 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, storage); } + else if (is_builtin && builtin == BuiltInClipDistance) + { + // Declare the ClipDistance as [[user(clipN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, i); + } if (get_decoration_bitset(var.self).get(DecorationIndex)) { @@ -1707,6 +1727,8 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")), ";"); } + else if (flatten_from_ib_var) + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", to_name(var.self), "[", i, "];"); else statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); }); @@ -1790,6 +1812,21 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass while (is_array(*usable_type) || is_matrix(*usable_type)) usable_type = &get(usable_type->parent_type); + bool flatten_from_ib_var = false; + + if (storage == StorageClassOutput && builtin == BuiltInClipDistance) + { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(mbr_type_id); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_name(ib_type.self, clip_array_mbr_idx, builtin_to_glsl(BuiltInClipDistance, StorageClassOutput)); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + } + for (uint32_t i = 0; i < elem_cnt; i++) { // Add a reference to the variable type to the interface struct. @@ -1818,6 +1855,12 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); mark_location_as_used_by_shader(locn, storage); } + else if (is_builtin && builtin == BuiltInClipDistance) + { + // Declare the ClipDistance as [[user(clipN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, i); + } if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays make little sense."); @@ -1849,8 +1892,16 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass case StorageClassOutput: entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() { - statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".", - to_member_name(var_type, mbr_idx), "[", i, "];"); + if (flatten_from_ib_var) + { + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", + to_member_name(var_type, mbr_idx), "[", i, "];"); + } + else + { + statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".", + to_member_name(var_type, mbr_idx), "[", i, "];"); + } }); break; @@ -2148,10 +2199,15 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st if (!is_builtin || has_active_builtin(builtin, storage)) { - if ((!is_builtin || - (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) && - (storage == StorageClassInput || storage == StorageClassOutput) && - (is_matrix(mbr_type) || is_array(mbr_type))) + bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type); + bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + bool storage_is_stage_io = storage == StorageClassInput || storage == StorageClassOutput; + + // ClipDistance always needs to be declared as user attributes. + if (builtin == BuiltInClipDistance) + is_builtin = false; + + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) { add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, strip_array); @@ -2175,10 +2231,17 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st { if (!is_builtin || has_active_builtin(builtin, storage)) { + bool is_composite_type = is_matrix(var_type) || is_array(var_type); + bool storage_is_stage_io = + storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer); + bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + + // ClipDistance always needs to be declared as user attributes. + if (builtin == BuiltInClipDistance) + is_builtin = false; + // MSL does not allow matrices or arrays in input or output variables, so need to handle it specially. - if ((!is_builtin || (storage == StorageClassInput && get_execution_model() != ExecutionModelFragment)) && - (storage == StorageClassInput || (storage == StorageClassOutput && !capture_output_to_buffer)) && - (is_matrix(var_type) || is_array(var_type))) + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) { add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, strip_array); } @@ -2266,6 +2329,11 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) bool filter_patch_decoration = (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch; bool hidden = is_hidden_variable(var, incl_builtins); + + // ClipDistance is never hidden, we need to emulate it when used as an input. + if (bi_type == BuiltInClipDistance) + hidden = false; + // Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments. if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV)) { @@ -8421,9 +8489,14 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in /* fallthrough */ case BuiltInPosition: case BuiltInLayer: - case BuiltInClipDistance: return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + case BuiltInClipDistance: + if (has_member_decoration(type.self, index, DecorationLocation)) + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationLocation), ")]]"); + else + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + default: return ""; } @@ -8521,6 +8594,9 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in quals = builtin_qualifier(builtin); break; + case BuiltInClipDistance: + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationLocation), ")]]"); + default: break; } @@ -11958,7 +12034,7 @@ void CompilerMSL::MemberSorter::sort() size_t mbr_cnt = type.member_types.size(); SmallVector mbr_idxs(mbr_cnt); iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices - std::sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect + std::stable_sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect // Move type and meta member info to the order defined by the sorted member indices. // This is done by creating temporary copies of both member types and meta, and then