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.
This commit is contained in:
Chip Davis 2019-07-10 22:42:39 -05:00
parent 9f3bebe3d0
commit 1df47db6ba
8 changed files with 108 additions and 3 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<pair<SPIRVariable *, BuiltIn>, 8> active_builtins;
ir.for_each_typed_id<SPIRVariable>([&](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());