From 7eecf5a46b3358a0b1b8ed405bc486524a0d99b7 Mon Sep 17 00:00:00 2001 From: Chip Davis Date: Fri, 31 May 2019 12:06:20 -0500 Subject: [PATCH] MSL: Support SPV_KHR_multiview. This is needed to support `VK_KHR_multiview`, which is in turn needed for Vulkan 1.1 support. Unfortunately, Metal provides no native support for this, and Apple is once again less than forthcoming, so we have to implement it all ourselves. Tessellation and geometry shaders are deliberately unsupported for now. The problem is that the current implementation encodes the `ViewIndex` as part of the `InstanceIndex`, which in the SPIR-V environment at least only exists in the vertex shader. So we need to work out a way to pass the view index along to the later stages. This implementation runs vertex shaders for all views up to the highest bit set in the view mask, even those whose bits are clear. The fragments for the inactive views are then discarded. Avoiding this is difficult: calculating the view indices becomes far more complicated if we can only run for those views which are set in the mask. --- main.cpp | 4 + .../frag/basic.multiview.nocompat.vk.frag | 32 +++++ .../vert/multiview.multiview.nocompat.vk.vert | 31 ++++ .../vulkan/vert/multiview.nocompat.vk.vert | 29 ++++ .../frag/basic.multiview.nocompat.vk.frag | 32 +++++ .../vert/multiview.multiview.nocompat.vk.vert | 31 ++++ .../vulkan/vert/multiview.nocompat.vk.vert | 29 ++++ .../frag/basic.multiview.nocompat.vk.frag | 14 ++ .../vert/multiview.multiview.nocompat.vk.vert | 14 ++ .../vulkan/vert/multiview.nocompat.vk.vert | 14 ++ spirv_msl.cpp | 135 +++++++++++++++++- spirv_msl.hpp | 12 ++ test_shaders.py | 2 + 13 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 shaders-msl/vulkan/vert/multiview.nocompat.vk.vert diff --git a/main.cpp b/main.cpp index 5931aef3..c441d235 100644 --- a/main.cpp +++ b/main.cpp @@ -514,6 +514,7 @@ struct CLIArguments bool msl_domain_lower_left = false; bool msl_argument_buffers = false; bool msl_texture_buffer_native = false; + bool msl_multiview = false; bool glsl_emit_push_constant_as_ubo = false; bool glsl_emit_ubo_as_plain_uniforms = false; bool emit_line_directives = false; @@ -592,6 +593,7 @@ static void print_help() "\t[--msl-argument-buffers]\n" "\t[--msl-texture-buffer-native]\n" "\t[--msl-discrete-descriptor-set ]\n" + "\t[--msl-multiview]\n" "\t[--hlsl]\n" "\t[--reflect]\n" "\t[--shader-model]\n" @@ -750,6 +752,7 @@ static string compile_iteration(const CLIArguments &args, std::vector msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left; msl_opts.argument_buffers = args.msl_argument_buffers; msl_opts.texture_buffer_native = args.msl_texture_buffer_native; + msl_opts.multiview = args.msl_multiview; msl_comp->set_msl_options(msl_opts); for (auto &v : args.msl_discrete_descriptor_sets) msl_comp->add_discrete_descriptor_set(v); @@ -1069,6 +1072,7 @@ static int main_inner(int argc, char *argv[]) cbs.add("--msl-discrete-descriptor-set", [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); }); cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; }); + cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = 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(); diff --git a/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 00000000..23c55494 --- /dev/null +++ b/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + float2 vTex[4] = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 00000000..c42e6721 --- /dev/null +++ b/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint gl_ViewIndex = spvViewMask[0] + gl_InstanceIndex % spvViewMask[1]; + gl_InstanceIndex /= spvViewMask[1]; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + out.gl_Layer = gl_ViewIndex - spvViewMask[0]; + return out; +} + diff --git a/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 00000000..f87d2a11 --- /dev/null +++ b/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = 0; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 00000000..23c55494 --- /dev/null +++ b/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + float2 vTex[4] = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 00000000..c42e6721 --- /dev/null +++ b/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint gl_ViewIndex = spvViewMask[0] + gl_InstanceIndex % spvViewMask[1]; + gl_InstanceIndex /= spvViewMask[1]; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + out.gl_Layer = gl_ViewIndex - spvViewMask[0]; + return out; +} + diff --git a/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 00000000..f87d2a11 --- /dev/null +++ b/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = 0; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 00000000..963493b8 --- /dev/null +++ b/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex[4]; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex[gl_ViewIndex]); +} + diff --git a/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 00000000..eb1bc766 --- /dev/null +++ b/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 00000000..eb1bc766 --- /dev/null +++ b/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 30bd4fb5..ef995fca 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -105,8 +105,10 @@ void CompilerMSL::build_implicit_builtins() active_input_builtins.get(BuiltInSubgroupLtMask); bool need_subgroup_ge_mask = !msl_options.is_ios() && (active_input_builtins.get(BuiltInSubgroupGeMask) || active_input_builtins.get(BuiltInSubgroupGtMask)); + bool need_multiview = get_execution_model() == ExecutionModelVertex && + (msl_options.multiview || active_input_builtins.get(BuiltInViewIndex)); if (need_subpass_input || need_sample_pos || need_subgroup_mask || need_vertex_params || need_tesc_params || - needs_subgroup_invocation_id) + need_multiview || needs_subgroup_invocation_id) { bool has_frag_coord = false; bool has_sample_id = false; @@ -118,6 +120,7 @@ void CompilerMSL::build_implicit_builtins() bool has_primitive_id = false; bool has_subgroup_invocation_id = false; bool has_subgroup_size = false; + bool has_view_idx = false; ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { if (var.storage != StorageClassInput || !ir.meta[var.self].decoration.builtin) @@ -189,6 +192,22 @@ void CompilerMSL::build_implicit_builtins() builtin_subgroup_size_id = var.self; has_subgroup_size = true; } + + if (need_multiview) + { + if (builtin == BuiltInInstanceIndex) + { + // The view index here is derived from the instance index. + builtin_instance_idx_id = var.self; + has_instance_idx = true; + } + + if (builtin == BuiltInViewIndex) + { + builtin_view_idx_id = var.self; + has_view_idx = true; + } + } }); if (!has_frag_coord && need_subpass_input) @@ -246,7 +265,8 @@ void CompilerMSL::build_implicit_builtins() mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var_id); } - if (need_vertex_params && (!has_vertex_idx || !has_base_vertex || !has_instance_idx || !has_base_instance)) + if ((need_vertex_params && (!has_vertex_idx || !has_base_vertex || !has_instance_idx || !has_base_instance)) || + (need_multiview && (!has_instance_idx || !has_view_idx))) { uint32_t offset = ir.increase_bound_by(2); uint32_t type_id = offset; @@ -265,7 +285,7 @@ void CompilerMSL::build_implicit_builtins() auto &ptr_type = set(type_ptr_id, uint_type_ptr); ptr_type.self = type_id; - if (!has_vertex_idx) + if (need_vertex_params && !has_vertex_idx) { uint32_t var_id = ir.increase_bound_by(1); @@ -276,7 +296,7 @@ void CompilerMSL::build_implicit_builtins() mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var_id); } - if (!has_base_vertex) + if (need_vertex_params && !has_base_vertex) { uint32_t var_id = ir.increase_bound_by(1); @@ -287,7 +307,7 @@ void CompilerMSL::build_implicit_builtins() mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var_id); } - if (!has_instance_idx) + if (!has_instance_idx) // Needed by both multiview and tessellation { uint32_t var_id = ir.increase_bound_by(1); @@ -296,9 +316,30 @@ void CompilerMSL::build_implicit_builtins() set_decoration(var_id, DecorationBuiltIn, BuiltInInstanceIndex); builtin_instance_idx_id = var_id; mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var_id); + + if (need_multiview) + { + // Multiview shaders are not allowed to write to gl_Layer, ostensibly because + // it is implicitly written from gl_ViewIndex, but we have to do that explicitly. + // Note that we can't just abuse gl_ViewIndex for this purpose: it's an input, but + // gl_Layer is an output in vertex-pipeline shaders. + uint32_t type_ptr_out_id = ir.increase_bound_by(2); + SPIRType uint_type_ptr_out; + uint_type_ptr_out = uint_type; + uint_type_ptr_out.pointer = true; + uint_type_ptr_out.parent_type = type_id; + uint_type_ptr_out.storage = StorageClassOutput; + auto &ptr_out_type = set(type_ptr_out_id, uint_type_ptr_out); + ptr_out_type.self = type_id; + var_id = type_ptr_out_id + 1; + set(var_id, type_ptr_out_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLayer); + builtin_layer_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInLayer, var_id); + } } - if (!has_base_instance) + if (need_vertex_params && !has_base_instance) { uint32_t var_id = ir.increase_bound_by(1); @@ -308,6 +349,17 @@ void CompilerMSL::build_implicit_builtins() builtin_base_instance_id = var_id; mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var_id); } + + if (need_multiview && !has_view_idx) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_ViewIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInViewIndex); + builtin_view_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var_id); + } } if (need_tesc_params && (!has_invocation_id || !has_primitive_id)) @@ -428,6 +480,17 @@ void CompilerMSL::build_implicit_builtins() set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.buffer_size_buffer_index); buffer_size_buffer_id = var_id; } + + if (needs_view_mask_buffer()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvViewMask"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(4u)); + set_decoration(var_id, DecorationBinding, msl_options.view_mask_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.view_mask_buffer_index); + view_mask_buffer_id = var_id; + } } void CompilerMSL::mark_implicit_builtin(StorageClass storage, BuiltIn builtin, uint32_t id) @@ -732,6 +795,10 @@ string CompilerMSL::compile() active_interface_variables.insert(swizzle_buffer_id); if (buffer_size_buffer_id) active_interface_variables.insert(buffer_size_buffer_id); + if (view_mask_buffer_id) + active_interface_variables.insert(view_mask_buffer_id); + if (builtin_layer_id) + active_interface_variables.insert(builtin_layer_id); // Create structs to hold input, output and uniform variables. // Do output first to ensure out. is declared at top of entry function. @@ -5700,6 +5767,10 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in { switch (builtin) { + case BuiltInViewIndex: + if (!msl_options.multiview) + break; + /* fallthrough */ case BuiltInFrontFacing: case BuiltInPointCoord: case BuiltInFragCoord: @@ -6087,7 +6158,9 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args) bi_type != BuiltInClipDistance && bi_type != BuiltInCullDistance && bi_type != BuiltInSubgroupEqMask && bi_type != BuiltInBaryCoordNV && bi_type != BuiltInBaryCoordNoPerspNV && bi_type != BuiltInSubgroupGeMask && bi_type != BuiltInSubgroupGtMask && - bi_type != BuiltInSubgroupLeMask && bi_type != BuiltInSubgroupLtMask) + bi_type != BuiltInSubgroupLeMask && bi_type != BuiltInSubgroupLtMask && + ((get_execution_model() == ExecutionModelFragment && msl_options.multiview) || + bi_type != BuiltInViewIndex)) { if (!ep_args.empty()) ep_args += ", "; @@ -6582,6 +6655,44 @@ void CompilerMSL::fix_up_shader_inputs_outputs() to_expression(builtin_subgroup_invocation_id_id), " - 32, 0)), uint2(0));"); }); break; + case BuiltInViewIndex: + if (!msl_options.multiview) + { + // According to the Vulkan spec, when not running under a multiview + // render pass, ViewIndex is 0. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = 0;"); + }); + } + else if (get_execution_model() == ExecutionModelFragment) + { + // Because we adjusted the view index in the vertex shader, we have to + // adjust it back here. + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), " += ", to_expression(view_mask_buffer_id), "[0];"); + }); + } + else if (get_execution_model() == ExecutionModelVertex) + { + // Metal provides no special support for multiview, so we smuggle + // the view index in the instance index. + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(view_mask_buffer_id), "[0] + ", to_expression(builtin_instance_idx_id), + " % ", to_expression(view_mask_buffer_id), "[1];"); + statement(to_expression(builtin_instance_idx_id), " /= ", to_expression(view_mask_buffer_id), + "[1];"); + }); + // In addition to setting the variable itself, we also need to + // set the render_target_array_index with it on output. We have to + // offset this by the base view index, because Metal isn't in on + // our little game here. + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_layer_id), " = ", to_expression(var_id), " - ", + to_expression(view_mask_buffer_id), "[0];"); + }); + } + break; default: break; } @@ -7883,6 +7994,12 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin) case BuiltInSamplePosition: // Shouldn't be reached. SPIRV_CROSS_THROW("Sample position is retrieved by a function in MSL."); + case BuiltInViewIndex: + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("ViewIndex is handled specially outside fragment shaders."); + // The ViewIndex was implicitly used in the prior stages to set the render_target_array_index, + // so we can get it from there. + return "render_target_array_index"; // Fragment function out case BuiltInFragDepth: @@ -8050,6 +8167,8 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) return "uint"; case BuiltInSamplePosition: return "float2"; + case BuiltInViewIndex: + return "uint"; // Fragment function out case BuiltInFragDepth: @@ -8634,6 +8753,7 @@ void CompilerMSL::bitcast_from_builtin_load(uint32_t source_id, std::string &exp case BuiltInPrimitiveId: case BuiltInSubgroupSize: case BuiltInSubgroupLocalInvocationId: + case BuiltInViewIndex: expected_type = SPIRType::UInt; break; @@ -8676,6 +8796,7 @@ void CompilerMSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr case BuiltInViewportIndex: case BuiltInFragStencilRefEXT: case BuiltInPrimitiveId: + case BuiltInViewIndex: expected_type = SPIRType::UInt; break; diff --git a/spirv_msl.hpp b/spirv_msl.hpp index e9abc197..15e731f7 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -191,12 +191,14 @@ public: uint32_t shader_patch_output_buffer_index = 27; uint32_t shader_tess_factor_buffer_index = 26; uint32_t buffer_size_buffer_index = 25; + uint32_t view_mask_buffer_index = 24; uint32_t shader_input_wg_index = 0; bool enable_point_size_builtin = true; bool disable_rasterization = false; bool capture_output_to_buffer = false; bool swizzle_texture_samples = false; bool tess_domain_origin_lower_left = false; + bool multiview = false; // Enable use of MSL 2.0 indirect argument buffers. // MSL 2.0 must also be enabled. @@ -268,6 +270,13 @@ public: return !buffers_requiring_array_length.empty(); } + // Provide feedback to calling API to allow it to pass a buffer + // containing the view mask for the current multiview subpass. + bool needs_view_mask_buffer() const + { + return msl_options.multiview; + } + // Provide feedback to calling API to allow it to pass an output // buffer if the shader needs it. bool needs_output_buffer() const @@ -526,12 +535,15 @@ protected: uint32_t builtin_base_vertex_id = 0; uint32_t builtin_instance_idx_id = 0; uint32_t builtin_base_instance_id = 0; + uint32_t builtin_view_idx_id = 0; + uint32_t builtin_layer_id = 0; uint32_t builtin_invocation_id_id = 0; uint32_t builtin_primitive_id_id = 0; uint32_t builtin_subgroup_invocation_id_id = 0; uint32_t builtin_subgroup_size_id = 0; uint32_t swizzle_buffer_id = 0; uint32_t buffer_size_buffer_id = 0; + uint32_t view_mask_buffer_id = 0; void bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; void bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; diff --git a/test_shaders.py b/test_shaders.py index 2df8e593..d2f75e80 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -203,6 +203,8 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths): msl_args.append('3') if '.line.' in shader: msl_args.append('--emit-line-directives') + if '.multiview.' in shader: + msl_args.append('--msl-multiview') subprocess.check_call(msl_args)