MSL: Adjust FragCoord for sample-rate shading.

In Metal, the `[[position]]` input to a fragment shader remains at
fragment center, even at sample rate, like OpenGL and Direct3D. In
Vulkan, however, when the fragment shader runs at sample rate, the
`FragCoord` builtin moves to the sample position in the framebuffer,
instead of the fragment center. To account for this difference, adjust
the `FragCoord`, if present, by the sample position. The -0.5 offset is
because the fragment center is at (0.5, 0.5).

Also, add an option to force sample-rate shading in a fragment shader.
Since Metal has no explicit control for this, this is done by adding a
dummy `[[sample_id]]` which is otherwise unused, if none is already
present. This is intended to be used from e.g. MoltenVK when a
pipeline's `minSampleShading` value is nonzero.

Instead of checking if any `Input` variables have `Sample`
interpolation, I've elected to check that the `SampleRateShading`
capability is present. Since `SampleId`, `SamplePosition`, and the
`Sample` interpolation decoration require this cap, this should be
equivalent for any valid SPIR-V module. If this isn't acceptable, let me
know.
This commit is contained in:
Chip Davis 2020-11-20 15:41:46 -06:00
parent 782916a797
commit fd738e3387
32 changed files with 320 additions and 8 deletions

View File

@ -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)

View File

@ -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 <size>]:\n\t\tAssign a constant <size> 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<uint32_t>
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();

View File

@ -0,0 +1,23 @@
#include <metal_stdlib>
#include <simd/simd.h>
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<float> 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;
}

View File

@ -11,6 +11,7 @@ struct main0_out
fragment main0_out main0(texture2d_ms_array<float> uSubpass0 [[texture(0)]], texture2d_ms_array<float> 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;
}

View File

@ -11,6 +11,7 @@ struct main0_out
fragment main0_out main0(texture2d_ms<float> uSubpass0 [[texture(0)]], texture2d_ms<float> 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;
}

View File

@ -11,6 +11,7 @@ struct main0_out
fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array<float> uSubpass0 [[texture(0)]], texture2d_ms_array<float> 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;

View File

@ -0,0 +1,19 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d_array<float> 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;
}

View File

@ -0,0 +1,24 @@
#include <metal_stdlib>
#include <simd/simd.h>
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<float> 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;
}

View File

@ -0,0 +1,19 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d<float> 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;
}

View File

@ -0,0 +1,18 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d<float> 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;
}

View File

@ -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<float> 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<float> 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);

View File

@ -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<float> 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<float> 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);

View File

@ -0,0 +1,23 @@
#include <metal_stdlib>
#include <simd/simd.h>
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<float> 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;
}

View File

@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms_array<float> uInput, thread uint
fragment main0_out main0(texture2d_ms_array<float> uSubpass0 [[texture(0)]], texture2d_ms_array<float> 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;
}

View File

@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms<float> uInput, thread uint& gl_S
fragment main0_out main0(texture2d_ms<float> uSubpass0 [[texture(0)]], texture2d_ms<float> 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;
}

View File

@ -20,6 +20,7 @@ float4 load_subpasses(thread const texture2d_ms_array<float> uInput, thread uint
fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array<float> uSubpass0 [[texture(0)]], texture2d_ms_array<float> 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;

View File

@ -0,0 +1,19 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d_array<float> 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;
}

View File

@ -0,0 +1,24 @@
#include <metal_stdlib>
#include <simd/simd.h>
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<float> 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;
}

View File

@ -0,0 +1,19 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d<float> 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;
}

View File

@ -0,0 +1,18 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
fragment main0_out main0(texture2d<float> 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;
}

View File

@ -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<float> 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<float> 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);

View File

@ -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<float> 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<float> 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);

View File

@ -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);
}

View File

@ -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)));
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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;

View File

@ -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.");

View File

@ -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);

View File

@ -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)