diff --git a/CMakeLists.txt b/CMakeLists.txt index 34515656..ea1c01a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,7 +323,7 @@ if (SPIRV_CROSS_STATIC) endif() set(spirv-cross-abi-major 0) -set(spirv-cross-abi-minor 43) +set(spirv-cross-abi-minor 44) set(spirv-cross-abi-patch 0) if (SPIRV_CROSS_SHARED) diff --git a/main.cpp b/main.cpp index f9ee2e0d..59f4bd50 100644 --- a/main.cpp +++ b/main.cpp @@ -569,6 +569,7 @@ struct CLIArguments bool msl_ios_use_simdgroup_functions = false; bool msl_emulate_subgroups = false; uint32_t msl_fixed_subgroup_size = 0; + bool msl_force_sample_rate_shading = false; bool glsl_emit_push_constant_as_ubo = false; bool glsl_emit_ubo_as_plain_uniforms = false; bool glsl_force_flattened_io_blocks = false; @@ -789,7 +790,9 @@ static void print_help_msl() "\t\tIntended for Vulkan Portability implementations where Metal support for SIMD-groups is insufficient for true subgroups.\n" "\t[--msl-fixed-subgroup-size ]:\n\t\tAssign a constant to the SubgroupSize builtin.\n" "\t\tIntended for Vulkan Portability implementations where VK_EXT_subgroup_size_control is not supported or disabled.\n" - "\t\tIf 0, assume variable subgroup size as actually exposed by Metal.\n"); + "\t\tIf 0, assume variable subgroup size as actually exposed by Metal.\n" + "\t[--msl-force-sample-rate-shading]:\n\t\tForce fragment shaders to run per sample.\n" + "\t\tThis adds a [[sample_id]] parameter if none is already present.\n"); // clang-format on } @@ -1034,6 +1037,7 @@ static string compile_iteration(const CLIArguments &args, std::vector msl_opts.ios_use_simdgroup_functions = args.msl_ios_use_simdgroup_functions; msl_opts.emulate_subgroups = args.msl_emulate_subgroups; msl_opts.fixed_subgroup_size = args.msl_fixed_subgroup_size; + msl_opts.force_sample_rate_shading = args.msl_force_sample_rate_shading; msl_comp->set_msl_options(msl_opts); for (auto &v : args.msl_discrete_descriptor_sets) msl_comp->add_discrete_descriptor_set(v); @@ -1466,6 +1470,7 @@ static int main_inner(int argc, char *argv[]) cbs.add("--msl-emulate-subgroups", [&args](CLIParser &) { args.msl_emulate_subgroups = true; }); cbs.add("--msl-fixed-subgroup-size", [&args](CLIParser &parser) { args.msl_fixed_subgroup_size = parser.next_uint(); }); + cbs.add("--msl-force-sample-rate-shading", [&args](CLIParser &) { args.msl_force_sample_rate_shading = true; }); cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); }); cbs.add("--rename-entry-point", [&args](CLIParser &parser) { auto old_name = parser.next_string(); diff --git a/reference/opt/shaders-msl/frag/basic.force-sample.frag b/reference/opt/shaders-msl/frag/basic.force-sample.frag new file mode 100644 index 00000000..b9706b73 --- /dev/null +++ b/reference/opt/shaders-msl/frag/basic.force-sample.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, in.vTex); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag b/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag index 5f137b04..52a78cf9 100644 --- a/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag +++ b/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag @@ -11,6 +11,7 @@ struct main0_out fragment main0_out main0(texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_Layer, 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, gl_SampleID); return out; } diff --git a/reference/opt/shaders-msl/frag/input-attachment-ms.frag b/reference/opt/shaders-msl/frag/input-attachment-ms.frag index 906cabbf..0c47348d 100644 --- a/reference/opt/shaders-msl/frag/input-attachment-ms.frag +++ b/reference/opt/shaders-msl/frag/input-attachment-ms.frag @@ -11,6 +11,7 @@ struct main0_out fragment main0_out main0(texture2d_ms uSubpass0 [[texture(0)]], texture2d_ms uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_SampleID); return out; } diff --git a/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag b/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag index 2e4ca8a7..e27b24ad 100644 --- a/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag +++ b/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag @@ -11,6 +11,7 @@ struct main0_out fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; gl_ViewIndex += spvViewMask[0]; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, gl_SampleID); return out; diff --git a/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag new file mode 100644 index 00000000..5df60f90 --- /dev/null +++ b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_array tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float3 _28 = float3(gl_FragCoord.xy, float(gl_SampleID)); + out.FragColor = tex.sample(texSmplr, _28.xy, uint(round(_28.z))); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag new file mode 100644 index 00000000..386230ef --- /dev/null +++ b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float foo [[user(locn0), sample_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d_array tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float3 _26 = float3(gl_FragCoord.xy, in.foo); + out.FragColor = tex.sample(texSmplr, _26.xy, uint(round(_26.z))); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag new file mode 100644 index 00000000..f8f357fe --- /dev/null +++ b/reference/opt/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + out.FragColor = tex.sample(texSmplr, (gl_FragCoord.xy - gl_SamplePosition)); + return out; +} + diff --git a/reference/opt/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag b/reference/opt/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag new file mode 100644 index 00000000..1ed8148d --- /dev/null +++ b/reference/opt/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + out.FragColor = tex.sample(texSmplr, gl_FragCoord.xy); + return out; +} + diff --git a/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag b/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag index 817b1cff..8bffca17 100644 --- a/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag +++ b/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag @@ -197,9 +197,10 @@ struct main0_out float4 out_var_SV_Target0 [[color(0)]]; }; -fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; float _68 = _67.w; float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); diff --git a/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag b/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag index 817b1cff..8bffca17 100644 --- a/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag +++ b/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag @@ -197,9 +197,10 @@ struct main0_out float4 out_var_SV_Target0 [[color(0)]]; }; -fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; float _68 = _67.w; float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); diff --git a/reference/shaders-msl/frag/basic.force-sample.frag b/reference/shaders-msl/frag/basic.force-sample.frag new file mode 100644 index 00000000..b9706b73 --- /dev/null +++ b/reference/shaders-msl/frag/basic.force-sample.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, in.vTex); + return out; +} + diff --git a/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag b/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag index 30ad95be..f7b1441f 100644 --- a/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag +++ b/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag @@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms_array uInput, thread uint fragment main0_out main0(texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_Layer, 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord, gl_Layer); return out; } diff --git a/reference/shaders-msl/frag/input-attachment-ms.frag b/reference/shaders-msl/frag/input-attachment-ms.frag index 25d2e7d3..5e1f504a 100644 --- a/reference/shaders-msl/frag/input-attachment-ms.frag +++ b/reference/shaders-msl/frag/input-attachment-ms.frag @@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms uInput, thread uint& gl_S fragment main0_out main0(texture2d_ms uSubpass0 [[texture(0)]], texture2d_ms uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord); return out; } diff --git a/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag b/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag index a2e5ef7d..5e8c5339 100644 --- a/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag +++ b/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag @@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms_array uInput, thread uint fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; gl_ViewIndex += spvViewMask[0]; out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord, gl_ViewIndex); return out; diff --git a/reference/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag new file mode 100644 index 00000000..5df60f90 --- /dev/null +++ b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_array tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float3 _28 = float3(gl_FragCoord.xy, float(gl_SampleID)); + out.FragColor = tex.sample(texSmplr, _28.xy, uint(round(_28.z))); + return out; +} + diff --git a/reference/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag new file mode 100644 index 00000000..386230ef --- /dev/null +++ b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float foo [[user(locn0), sample_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d_array tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float3 _26 = float3(gl_FragCoord.xy, in.foo); + out.FragColor = tex.sample(texSmplr, _26.xy, uint(round(_26.z))); + return out; +} + diff --git a/reference/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag new file mode 100644 index 00000000..f8f357fe --- /dev/null +++ b/reference/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + out.FragColor = tex.sample(texSmplr, (gl_FragCoord.xy - gl_SamplePosition)); + return out; +} + diff --git a/reference/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag b/reference/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag new file mode 100644 index 00000000..1ed8148d --- /dev/null +++ b/reference/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d tex [[texture(0)]], sampler texSmplr [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; + out.FragColor = tex.sample(texSmplr, gl_FragCoord.xy); + return out; +} + diff --git a/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag b/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag index 817b1cff..8bffca17 100644 --- a/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag +++ b/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag @@ -197,9 +197,10 @@ struct main0_out float4 out_var_SV_Target0 [[color(0)]]; }; -fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; float _68 = _67.w; float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); diff --git a/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag b/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag index 817b1cff..8bffca17 100644 --- a/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag +++ b/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag @@ -197,9 +197,10 @@ struct main0_out float4 out_var_SV_Target0 [[color(0)]]; }; -fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleID [[sample_id]]) { main0_out out = {}; + gl_FragCoord.xy += get_sample_position(gl_SampleID) - 0.5; float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; float _68 = _67.w; float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); diff --git a/shaders-msl/frag/basic.force-sample.frag b/shaders-msl/frag/basic.force-sample.frag new file mode 100644 index 00000000..dd9a8f85 --- /dev/null +++ b/shaders-msl/frag/basic.force-sample.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag b/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag new file mode 100644 index 00000000..202dba0b --- /dev/null +++ b/shaders-msl/frag/sample-rate-frag-coord-sample-id.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DArray tex; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, vec3(gl_FragCoord.xy, float(gl_SampleID))); +} diff --git a/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag b/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag new file mode 100644 index 00000000..b131fb03 --- /dev/null +++ b/shaders-msl/frag/sample-rate-frag-coord-sample-input.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DArray tex; + +layout(location = 0) sample in float foo; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, vec3(gl_FragCoord.xy, foo)); +} diff --git a/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag b/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag new file mode 100644 index 00000000..c8c3be96 --- /dev/null +++ b/shaders-msl/frag/sample-rate-frag-coord-sample-pos.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D tex; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, gl_FragCoord.xy - gl_SamplePosition); +} diff --git a/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag b/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag new file mode 100644 index 00000000..a6b47e4b --- /dev/null +++ b/shaders-msl/frag/sample-rate-frag-coord.force-sample.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D tex; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(tex, gl_FragCoord.xy); +} diff --git a/spirv_cross_c.cpp b/spirv_cross_c.cpp index 4b561378..d90d40ab 100644 --- a/spirv_cross_c.cpp +++ b/spirv_cross_c.cpp @@ -690,6 +690,10 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c case SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE: options->msl.fixed_subgroup_size = value; break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING: + options->msl.force_sample_rate_shading = value != 0; + break; #endif default: diff --git a/spirv_cross_c.h b/spirv_cross_c.h index e12c2eaf..88f04aaa 100644 --- a/spirv_cross_c.h +++ b/spirv_cross_c.h @@ -33,7 +33,7 @@ extern "C" { /* Bumped if ABI or API breaks backwards compatibility. */ #define SPVC_C_API_VERSION_MAJOR 0 /* Bumped if APIs or enumerations are added in a backwards compatible way. */ -#define SPVC_C_API_VERSION_MINOR 43 +#define SPVC_C_API_VERSION_MINOR 44 /* Bumped if internal implementation details change. */ #define SPVC_C_API_VERSION_PATCH 0 @@ -650,6 +650,7 @@ typedef enum spvc_compiler_option SPVC_COMPILER_OPTION_MSL_IOS_USE_SIMDGROUP_FUNCTIONS = 72 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_EMULATE_SUBGROUPS = 73 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE = 74 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT, SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff } spvc_compiler_option; diff --git a/spirv_msl.cpp b/spirv_msl.cpp index c2916729..9f936be0 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -1354,7 +1354,12 @@ void CompilerMSL::preprocess_op_codes() needs_subgroup_invocation_id = true; if (preproc.needs_subgroup_size) needs_subgroup_size = true; - if (preproc.needs_sample_id) + // build_implicit_builtins() hasn't run yet, and in fact, this needs to execute + // before then so that gl_SampleID will get added; so we also need to check if + // that function would add gl_FragCoord. + if (preproc.needs_sample_id || msl_options.force_sample_rate_shading || + (is_sample_rate() && (active_input_builtins.get(BuiltInFragCoord) || + (need_subpass_input && !msl_options.use_framebuffer_fetch_subpasses)))) needs_sample_id = true; } @@ -10495,6 +10500,16 @@ bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type) } } +// Returns true if this is a fragment shader that runs per sample, and false otherwise. +bool CompilerMSL::is_sample_rate() const +{ + auto &caps = get_declared_capabilities(); + return get_execution_model() == ExecutionModelFragment && + (msl_options.force_sample_rate_shading || + std::find(caps.begin(), caps.end(), CapabilitySampleRateShading) != caps.end() || + (msl_options.use_framebuffer_fetch_subpasses && need_subpass_input)); +} + void CompilerMSL::entry_point_args_builtin(string &ep_args) { // Builtin variables @@ -11047,6 +11062,15 @@ void CompilerMSL::fix_up_shader_inputs_outputs() to_expression(builtin_sample_id_id), ");"); }); break; + case BuiltInFragCoord: + if (is_sample_rate()) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), ".xy += get_sample_position(", + to_expression(builtin_sample_id_id), ") - 0.5;"); + }); + } + break; case BuiltInHelperInvocation: if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.3 on iOS."); diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 0b78df47..61d4fbea 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -401,6 +401,11 @@ public: // different shaders for these three scenarios. IndexType vertex_index_type = IndexType::None; + // If set, a dummy [[sample_id]] input is added to a fragment shader if none is present. + // This will force the shader to run at sample rate, assuming Metal does not optimize + // the extra threads away. + bool force_sample_rate_shading = false; + bool is_ios() const { return platform == iOS; @@ -798,6 +803,7 @@ protected: std::string to_sampler_expression(uint32_t id); std::string to_swizzle_expression(uint32_t id); std::string to_buffer_size_expression(uint32_t id); + bool is_sample_rate() const; bool is_direct_input_builtin(spv::BuiltIn builtin); std::string builtin_qualifier(spv::BuiltIn builtin); std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0); diff --git a/test_shaders.py b/test_shaders.py index d73f8598..dd831e91 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -316,6 +316,8 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths): # Arbitrary for testing purposes. msl_args.append('--msl-fixed-subgroup-size') msl_args.append('32') + if '.force-sample.' in shader: + msl_args.append('--msl-force-sample-rate-shading') subprocess.check_call(msl_args)