MSL: Add options to control emission of fragment outputs.

Like with `point_size` when not rendering points, Metal complains when
writing to a variable using the `[[depth]]` qualifier when no depth
buffer be attached. In that case, we must avoid emitting `FragDepth`,
just like with `PointSize`.

I assume it will also complain if there be no stencil attachment and the
shader write to `[[stencil]]`, or it write to `[[color(n)]]` but there
be no color attachment at n.
This commit is contained in:
Chip Davis 2020-04-10 01:13:33 -05:00
parent fcbc590937
commit b29f83c383
10 changed files with 201 additions and 6 deletions

View File

@ -323,7 +323,7 @@ if (SPIRV_CROSS_STATIC)
endif()
set(spirv-cross-abi-major 0)
set(spirv-cross-abi-minor 30)
set(spirv-cross-abi-minor 31)
set(spirv-cross-abi-patch 0)
if (SPIRV_CROSS_SHARED)

View File

@ -136,6 +136,25 @@ struct CLIParser
return uint32_t(val);
}
uint32_t next_hex_uint()
{
if (!argc)
{
THROW("Tried to parse uint, but nothing left in arguments");
}
uint64_t val = stoul(*argv, nullptr, 16);
if (val > numeric_limits<uint32_t>::max())
{
THROW("next_uint() out of range");
}
argc--;
argv++;
return uint32_t(val);
}
double next_double()
{
if (!argc)
@ -535,6 +554,9 @@ struct CLIArguments
bool msl_decoration_binding = false;
bool msl_force_active_argument_buffer_resources = false;
bool msl_force_native_arrays = false;
bool msl_enable_frag_depth_builtin = true;
bool msl_enable_frag_stencil_ref_builtin = true;
uint32_t msl_enable_frag_output_mask = 0xffffffff;
bool glsl_emit_push_constant_as_ubo = false;
bool glsl_emit_ubo_as_plain_uniforms = false;
SmallVector<pair<uint32_t, uint32_t>> glsl_ext_framebuffer_fetch;
@ -638,6 +660,9 @@ static void print_help()
"\t[--msl-decoration-binding]\n"
"\t[--msl-force-active-argument-buffer-resources]\n"
"\t[--msl-force-native-arrays]\n"
"\t[--msl-disable-frag-depth-builtin]\n"
"\t[--msl-disable-frag-stencil-ref-builtin]\n"
"\t[--msl-enable-frag-output-mask <mask>]\n"
"\t[--hlsl]\n"
"\t[--reflect]\n"
"\t[--shader-model]\n"
@ -831,6 +856,9 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
msl_opts.enable_decoration_binding = args.msl_decoration_binding;
msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources;
msl_opts.force_native_arrays = args.msl_force_native_arrays;
msl_opts.enable_frag_depth_builtin = args.msl_enable_frag_depth_builtin;
msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin;
msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask;
msl_comp->set_msl_options(msl_opts);
for (auto &v : args.msl_discrete_descriptor_sets)
msl_comp->add_discrete_descriptor_set(v);
@ -1213,6 +1241,11 @@ static int main_inner(int argc, char *argv[])
args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding));
});
cbs.add("--msl-force-native-arrays", [&args](CLIParser &) { args.msl_force_native_arrays = true; });
cbs.add("--msl-disable-frag-depth-builtin", [&args](CLIParser &) { args.msl_enable_frag_depth_builtin = false; });
cbs.add("--msl-disable-frag-stencil-ref-builtin",
[&args](CLIParser &) { args.msl_enable_frag_stencil_ref_builtin = false; });
cbs.add("--msl-enable-frag-output-mask",
[&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
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,35 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 buf1 [[color(1)]];
float4 buf3 [[color(3)]];
float4 buf6 [[color(6)]];
float4 buf7 [[color(7)]];
};
fragment main0_out main0()
{
float4 buf0;
float4 buf2;
float4 buf4;
float4 buf5;
float gl_FragDepth;
int gl_FragStencilRefARB;
main0_out out = {};
buf0 = float4(0.0, 0.0, 0.0, 1.0);
out.buf1 = float4(1.0, 0.0, 0.0, 1.0);
buf2 = float4(0.0, 1.0, 0.0, 1.0);
out.buf3 = float4(0.0, 0.0, 1.0, 1.0);
buf4 = float4(1.0, 0.0, 1.0, 0.5);
buf5 = float4(0.25);
out.buf6 = float4(0.75);
out.buf7 = float4(1.0);
gl_FragDepth = 0.89999997615814208984375;
gl_FragStencilRefARB = uint(127);
return out;
}

View File

@ -0,0 +1,35 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 buf1 [[color(1)]];
float4 buf3 [[color(3)]];
float4 buf6 [[color(6)]];
float4 buf7 [[color(7)]];
};
fragment main0_out main0()
{
float4 buf0;
float4 buf2;
float4 buf4;
float4 buf5;
float gl_FragDepth;
int gl_FragStencilRefARB;
main0_out out = {};
buf0 = float4(0.0, 0.0, 0.0, 1.0);
out.buf1 = float4(1.0, 0.0, 0.0, 1.0);
buf2 = float4(0.0, 1.0, 0.0, 1.0);
out.buf3 = float4(0.0, 0.0, 1.0, 1.0);
buf4 = float4(1.0, 0.0, 1.0, 0.5);
buf5 = float4(0.25);
out.buf6 = float4(0.75);
out.buf7 = float4(1.0);
gl_FragDepth = 0.89999997615814208984375;
gl_FragStencilRefARB = uint(127);
return out;
}

View File

@ -0,0 +1,25 @@
#version 450
#extension GL_ARB_shader_stencil_export : require
layout(location = 0) out vec4 buf0;
layout(location = 1) out vec4 buf1;
layout(location = 2) out vec4 buf2;
layout(location = 3) out vec4 buf3;
layout(location = 4) out vec4 buf4;
layout(location = 5) out vec4 buf5;
layout(location = 6) out vec4 buf6;
layout(location = 7) out vec4 buf7;
void main() {
buf0 = vec4(0, 0, 0, 1);
buf1 = vec4(1, 0, 0, 1);
buf2 = vec4(0, 1, 0, 1);
buf3 = vec4(0, 0, 1, 1);
buf4 = vec4(1, 0, 1, 0.5);
buf5 = vec4(0.25, 0.25, 0.25, 0.25);
buf6 = vec4(0.75, 0.75, 0.75, 0.75);
buf7 = vec4(1, 1, 1, 1);
gl_FragDepth = 0.9;
gl_FragStencilRefARB = 127;
}

View File

@ -615,6 +615,18 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
case SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS:
options->msl.force_native_arrays = value != 0;
break;
case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK:
options->msl.enable_frag_output_mask = value;
break;
case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN:
options->msl.enable_frag_depth_builtin = value != 0;
break;
case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN:
options->msl.enable_frag_stencil_ref_builtin = 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 30
#define SPVC_C_API_VERSION_MINOR 31
/* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0
@ -582,6 +582,10 @@ typedef enum spvc_compiler_option
SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV = 55 | SPVC_COMPILER_OPTION_HLSL_BIT,
SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK = 56 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN = 57 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN = 58 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} spvc_compiler_option;

View File

@ -965,6 +965,16 @@ void CompilerMSL::emit_entry_point_declarations()
}
// For some reason, without this, we end up emitting the arrays twice.
buffer_arrays.clear();
// Emit disabled fragment outputs.
std::sort(disabled_frag_outputs.begin(), disabled_frag_outputs.end());
for (uint32_t var_id : disabled_frag_outputs)
{
auto &var = get<SPIRVariable>(var_id);
add_local_variable_name(var_id);
statement(variable_decl(var), ";");
var.deferred_declaration = false;
}
}
string CompilerMSL::compile()
@ -2442,6 +2452,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
bool is_builtin = is_builtin_variable(var);
auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn));
uint32_t location = get_decoration(var_id, DecorationLocation);
// These builtins are part of the stage in/out structs.
bool is_interface_block_builtin =
@ -2467,6 +2478,18 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
if (bi_type == BuiltInClipDistance)
hidden = false;
// It's not enough to simply avoid marking fragment outputs if the pipeline won't
// accept them. We can't put them in the struct at all, or otherwise the compiler
// complains that the outputs weren't explicitly marked.
if (storage == StorageClassOutput && !patch &&
((is_builtin && ((bi_type == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) ||
(bi_type == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))) ||
(!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location)))))
{
hidden = true;
disabled_frag_outputs.push_back(var_id);
}
// Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments.
if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV))
{
@ -2496,7 +2519,6 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
SPIRV_CROSS_THROW("Component decoration is not supported in tessellation shaders.");
else if (pack_components)
{
uint32_t location = get_decoration(var_id, DecorationLocation);
auto &location_meta = meta.location_meta[location];
location_meta.num_components = std::max(location_meta.num_components, component + type.vecsize);
}
@ -8877,12 +8899,23 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
switch (builtin)
{
case BuiltInFragStencilRefEXT:
// Similar to PointSize, only mark FragStencilRef if there's a stencil buffer.
// Some shaders may include a FragStencilRef builtin even when used to render
// without a stencil attachment, and Metal will reject this builtin
// when compiling the shader into a render pipeline that does not set
// stencilAttachmentPixelFormat.
if (!msl_options.enable_frag_stencil_ref_builtin)
return "";
if (!msl_options.supports_msl_version(2, 1))
SPIRV_CROSS_THROW("Stencil export only supported in MSL 2.1 and up.");
return string(" [[") + builtin_qualifier(builtin) + "]]";
case BuiltInSampleMask:
case BuiltInFragDepth:
// Ditto FragDepth.
if (!msl_options.enable_frag_depth_builtin)
return "";
/* fallthrough */
case BuiltInSampleMask:
return string(" [[") + builtin_qualifier(builtin) + "]]";
default:
@ -8890,6 +8923,9 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
}
uint32_t locn = get_ordered_member_location(type.self, index);
// Metal will likely complain about missing color attachments, too.
if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn)))
return "";
if (locn != k_unknown_location && has_member_decoration(type.self, index, DecorationIndex))
return join(" [[color(", locn, "), index(", get_member_decoration(type.self, index, DecorationIndex),
")]]");
@ -11345,13 +11381,17 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
if (!msl_options.supports_msl_version(2, 0))
SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0.");
/* fallthrough */
case BuiltInFragDepth:
case BuiltInFragStencilRefEXT:
if ((builtin == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) ||
(builtin == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))
break;
/* fallthrough */
case BuiltInPosition:
case BuiltInPointSize:
case BuiltInClipDistance:
case BuiltInCullDistance:
case BuiltInLayer:
case BuiltInFragDepth:
case BuiltInFragStencilRefEXT:
case BuiltInSampleMask:
if (get_execution_model() == ExecutionModelTessellationControl)
break;

View File

@ -268,7 +268,10 @@ public:
uint32_t dynamic_offsets_buffer_index = 23;
uint32_t shader_input_wg_index = 0;
uint32_t device_index = 0;
uint32_t enable_frag_output_mask = 0xffffffff;
bool enable_point_size_builtin = true;
bool enable_frag_depth_builtin = true;
bool enable_frag_stencil_ref_builtin = true;
bool disable_rasterization = false;
bool capture_output_to_buffer = false;
bool swizzle_texture_samples = false;
@ -870,6 +873,8 @@ protected:
// Must be ordered since array is in a specific order.
std::map<SetBindingPair, std::pair<uint32_t, uint32_t>> buffers_requiring_dynamic_offset;
SmallVector<uint32_t> disabled_frag_outputs;
std::unordered_set<SetBindingPair, InternalHasher> inline_uniform_blocks;
uint32_t argument_buffer_ids[kMaxArgumentBuffers];

View File

@ -258,6 +258,12 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths):
msl_args.append('--msl-force-native-arrays')
if '.zero-initialize.' in shader:
msl_args.append('--force-zero-initialized-variables')
if '.frag-output.' in shader:
# Arbitrary for testing purposes.
msl_args.append('--msl-disable-frag-depth-builtin')
msl_args.append('--msl-disable-frag-stencil-ref-builtin')
msl_args.append('--msl-enable-frag-output-mask')
msl_args.append('0x000000ca')
subprocess.check_call(msl_args)