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:
parent
fcbc590937
commit
b29f83c383
@ -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)
|
||||
|
33
main.cpp
33
main.cpp
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
25
shaders-msl/frag/disable-frag-output.frag-output.frag
Normal file
25
shaders-msl/frag/disable-frag-output.frag-output.frag
Normal 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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user