From 1df47db6ba9167c7ea58c651af9cf157c341a1c8 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Wed, 10 Jul 2019 22:42:39 -0500 Subject: [PATCH] Support the SPV_KHR_post_depth_coverage extension. Using the `PostDepthCoverage` mode specifies that the `gl_SampleMaskIn` variable is to contain the computed coverage mask following the early fragment tests, which this mode requires and implicitly enables. Note that unlike Vulkan and OpenGL, Metal places this on the sample mask input itself, and furthermore does *not* implicitly enable early fragment testing. If it isn't enabled explicitly with an `[[early_fragment_tests]]` attribute, the compiler will error out. So we have to enable that mode explicitly if `PostDepthCoverage` is enabled but `EarlyFragmentTests` isn't. For Metal, only iOS supports this; for some reason, Apple has yet to implement it on macOS, even though many desktop cards support it. --- .../frag/post-depth-coverage.ios.msl2.frag | 17 ++++++++++++ .../opt/shaders/frag/post-depth-coverage.frag | 11 ++++++++ .../frag/post-depth-coverage.ios.msl2.frag | 17 ++++++++++++ .../shaders/frag/post-depth-coverage.frag | 11 ++++++++ .../frag/post-depth-coverage.ios.msl2.frag | 11 ++++++++ shaders/frag/post-depth-coverage.frag | 11 ++++++++ spirv_glsl.cpp | 6 +++++ spirv_msl.cpp | 27 ++++++++++++++++--- 8 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 reference/opt/shaders/frag/post-depth-coverage.frag create mode 100644 reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 reference/shaders/frag/post-depth-coverage.frag create mode 100644 shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 shaders/frag/post-depth-coverage.frag diff --git a/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 00000000..3b2885e2 --- /dev/null +++ b/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/reference/opt/shaders/frag/post-depth-coverage.frag b/reference/opt/shaders/frag/post-depth-coverage.frag new file mode 100644 index 00000000..3e78fcd0 --- /dev/null +++ b/reference/opt/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 00000000..3b2885e2 --- /dev/null +++ b/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/reference/shaders/frag/post-depth-coverage.frag b/reference/shaders/frag/post-depth-coverage.frag new file mode 100644 index 00000000..3e78fcd0 --- /dev/null +++ b/reference/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 00000000..4f134b4f --- /dev/null +++ b/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require + +layout(post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/shaders/frag/post-depth-coverage.frag b/shaders/frag/post-depth-coverage.frag new file mode 100644 index 00000000..4f134b4f --- /dev/null +++ b/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require + +layout(post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 14d23863..74d8f51c 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -600,6 +600,10 @@ void CompilerGLSL::emit_header() require_extension_internal("GL_ARB_shader_image_load_store"); } + // Needed for: layout(post_depth_coverage) in; + if (execution.flags.get(ExecutionModePostDepthCoverage)) + require_extension_internal("GL_ARB_post_depth_coverage"); + for (auto &ext : forced_extensions) { if (ext == "GL_EXT_shader_explicit_arithmetic_types_float16") @@ -763,6 +767,8 @@ void CompilerGLSL::emit_header() if (execution.flags.get(ExecutionModeEarlyFragmentTests)) inputs.push_back("early_fragment_tests"); + if (execution.flags.get(ExecutionModePostDepthCoverage)) + inputs.push_back("post_depth_coverage"); if (!options.es && execution.flags.get(ExecutionModeDepthGreater)) statement("layout(depth_greater) out float gl_FragDepth;"); diff --git a/spirv_msl.cpp b/spirv_msl.cpp index eaee10a0..5b3a624a 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -6165,8 +6165,10 @@ string CompilerMSL::func_type_decl(SPIRType &type) execution.output_vertices, ") ]] vertex"); break; case ExecutionModelFragment: - entry_type = - execution.flags.get(ExecutionModeEarlyFragmentTests) ? "[[ early_fragment_tests ]] fragment" : "fragment"; + entry_type = execution.flags.get(ExecutionModeEarlyFragmentTests) || + execution.flags.get(ExecutionModePostDepthCoverage) ? + "[[ early_fragment_tests ]] fragment" : + "fragment"; break; case ExecutionModelTessellationControl: if (!msl_options.supports_msl_version(1, 2)) @@ -6330,6 +6332,7 @@ string CompilerMSL::entry_point_arg_stage_in() void CompilerMSL::entry_point_args_builtin(string &ep_args) { // Builtin variables + SmallVector, 8> active_builtins; ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); @@ -6344,6 +6347,9 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) if (!active_input_builtins.get(bi_type) || !interface_variable_exists_in_entry_point(var_id)) return; + // Remember this variable. We may need to correct its type. + active_builtins.push_back(make_pair(&var, bi_type)); + // These builtins are emitted specially. If we pass this branch, the builtin directly matches // a MSL builtin. if (bi_type != BuiltInSamplePosition && bi_type != BuiltInHelperInvocation && @@ -6363,11 +6369,26 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) ep_args += ", "; ep_args += builtin_type_decl(bi_type, var_id) + " " + to_expression(var_id); - ep_args += " [[" + builtin_qualifier(bi_type) + "]]"; + ep_args += " [[" + builtin_qualifier(bi_type); + if (bi_type == BuiltInSampleMask && get_entry_point().flags.get(ExecutionModePostDepthCoverage)) + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Post-depth coverage requires Metal 2.0."); + if (!msl_options.is_ios()) + SPIRV_CROSS_THROW("Post-depth coverage is only supported on iOS."); + ep_args += ", post_depth_coverage"; + } + ep_args += "]]"; } } }); + // Correct the types of all encountered active builtins. We couldn't do this before + // because ensure_correct_builtin_type() may increase the bound, which isn't allowed + // while iterating over IDs. + for (auto &var : active_builtins) + var.first->basetype = ensure_correct_builtin_type(var.first->basetype, var.second); + // Vertex and instance index built-ins if (needs_vertex_idx_arg) ep_args += built_in_func_arg(BuiltInVertexIndex, !ep_args.empty());