MSL: Don't set the layer for multiview if the device doesn't support it.

Some older iOS devices don't support layered rendering. In that case,
don't set `[[render_target_array_index]]`, because the compiler will
reject the shader in that case. The client will then have to unroll the
render pass manually.
This commit is contained in:
Chip Davis 2020-08-23 16:44:41 -05:00
parent 4752a44f9e
commit cab7335e64
13 changed files with 260 additions and 4 deletions

View File

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

View File

@ -549,6 +549,7 @@ struct CLIArguments
bool msl_invariant_float_math = false;
bool msl_emulate_cube_array = false;
bool msl_multiview = false;
bool msl_multiview_layered_rendering = true;
bool msl_view_index_from_device_index = false;
bool msl_dispatch_base = false;
bool msl_decoration_binding = false;
@ -732,6 +733,8 @@ static void print_help_msl()
"\t[--msl-device-argument-buffer <descriptor set index>]:\n\t\tUse device address space to hold indirect argument buffers instead of constant.\n"
"\t\tComes up when trying to support argument buffers which are larger than 64 KiB.\n"
"\t[--msl-multiview]:\n\t\tEnable SPV_KHR_multiview emulation.\n"
"\t[--msl-multiview-no-layered-rendering]:\n\t\tDon't set [[render_target_array_index]] in multiview shaders.\n"
"\t\tUseful for devices which don't support layered rendering. Only effective when --msl-multiview is enabled.\n"
"\t[--msl-view-index-from-device-index]:\n\t\tTreat the view index as the device index instead.\n"
"\t\tFor multi-GPU rendering.\n"
"\t[--msl-dispatch-base]:\n\t\tAdd support for vkCmdDispatchBase() or similar APIs.\n"
@ -987,6 +990,7 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
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_opts.multiview_layered_rendering = args.msl_multiview_layered_rendering;
msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index;
msl_opts.dispatch_base = args.msl_dispatch_base;
msl_opts.enable_decoration_binding = args.msl_decoration_binding;
@ -1366,6 +1370,8 @@ static int main_inner(int argc, char *argv[])
cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; });
cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; });
cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; });
cbs.add("--msl-multiview-no-layered-rendering",
[&args](CLIParser &) { args.msl_multiview_layered_rendering = false; });
cbs.add("--msl-view-index-from-device-index",
[&args](CLIParser &) { args.msl_view_index_from_device_index = true; });
cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; });

View File

@ -0,0 +1,73 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wmissing-braces"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
template<typename T, size_t Num>
struct spvUnsafeArray
{
T elements[Num ? Num : 1];
thread T& operator [] (size_t pos) thread
{
return elements[pos];
}
constexpr const thread T& operator [] (size_t pos) const thread
{
return elements[pos];
}
device T& operator [] (size_t pos) device
{
return elements[pos];
}
constexpr const device T& operator [] (size_t pos) const device
{
return elements[pos];
}
constexpr const constant T& operator [] (size_t pos) const constant
{
return elements[pos];
}
threadgroup T& operator [] (size_t pos) threadgroup
{
return elements[pos];
}
constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
{
return elements[pos];
}
};
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<float> uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]])
{
main0_out out = {};
spvUnsafeArray<float2, 4> vTex = {};
vTex[0] = in.vTex_0;
vTex[1] = in.vTex_1;
vTex[2] = in.vTex_2;
vTex[3] = in.vTex_3;
const uint gl_ViewIndex = spvViewMask[0];
out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]);
return out;
}

View File

@ -0,0 +1,28 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct MVPs
{
float4x4 MVP[2];
};
struct main0_out
{
float4 gl_Position [[position]];
};
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)]])
{
main0_out out = {};
const uint gl_ViewIndex = spvViewMask[0];
out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position;
return out;
}

View File

@ -0,0 +1,73 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#pragma clang diagnostic ignored "-Wmissing-braces"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
template<typename T, size_t Num>
struct spvUnsafeArray
{
T elements[Num ? Num : 1];
thread T& operator [] (size_t pos) thread
{
return elements[pos];
}
constexpr const thread T& operator [] (size_t pos) const thread
{
return elements[pos];
}
device T& operator [] (size_t pos) device
{
return elements[pos];
}
constexpr const device T& operator [] (size_t pos) const device
{
return elements[pos];
}
constexpr const constant T& operator [] (size_t pos) const constant
{
return elements[pos];
}
threadgroup T& operator [] (size_t pos) threadgroup
{
return elements[pos];
}
constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
{
return elements[pos];
}
};
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<float> uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]])
{
main0_out out = {};
spvUnsafeArray<float2, 4> vTex = {};
vTex[0] = in.vTex_0;
vTex[1] = in.vTex_1;
vTex[2] = in.vTex_2;
vTex[3] = in.vTex_3;
const uint gl_ViewIndex = spvViewMask[0];
out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]);
return out;
}

View File

@ -0,0 +1,28 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct MVPs
{
float4x4 MVP[2];
};
struct main0_out
{
float4 gl_Position [[position]];
};
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)]])
{
main0_out out = {};
const uint gl_ViewIndex = spvViewMask[0];
out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position;
return out;
}

View File

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

View File

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

View File

@ -658,6 +658,10 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
case SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE:
options->msl.vertex_index_type = static_cast<CompilerMSL::Options::IndexType>(value);
break;
case SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING:
options->msl.multiview_layered_rendering = 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 37
#define SPVC_C_API_VERSION_MINOR 38
/* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0
@ -636,6 +636,8 @@ typedef enum spvc_compiler_option
SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS = 66 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING = 67 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} spvc_compiler_option;

View File

@ -146,6 +146,7 @@ void CompilerMSL::build_implicit_builtins()
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.view_index_from_device_index &&
msl_options.multiview_layered_rendering &&
(msl_options.multiview || active_input_builtins.get(BuiltInViewIndex));
bool need_dispatch_base =
msl_options.dispatch_base && get_execution_model() == ExecutionModelGLCompute &&
@ -9242,7 +9243,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
switch (builtin)
{
case BuiltInViewIndex:
if (!msl_options.multiview)
if (!msl_options.multiview || !msl_options.multiview_layered_rendering)
break;
/* fallthrough */
case BuiltInFrontFacing:
@ -9660,7 +9661,8 @@ bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type)
case BuiltInBaryCoordNoPerspNV:
return false;
case BuiltInViewIndex:
return get_execution_model() == ExecutionModelFragment && msl_options.multiview;
return get_execution_model() == ExecutionModelFragment && msl_options.multiview &&
msl_options.multiview_layered_rendering;
// Any stage function in
case BuiltInDeviceIndex:
case BuiltInSubgroupEqMask:
@ -10430,6 +10432,15 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
// Since every physical device is rendering a different view,
// there's no need for layered rendering here.
}
else if (!msl_options.multiview_layered_rendering)
{
// In this case, the views are rendered one at a time. The view index, then,
// is just the first part of the "view mask".
entry_func.fixup_hooks_in.push_back([=]() {
statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ",
to_expression(view_mask_buffer_id), "[0];");
});
}
else if (get_execution_model() == ExecutionModelFragment)
{
// Because we adjusted the view index in the vertex shader, we have to

View File

@ -290,6 +290,7 @@ public:
bool swizzle_texture_samples = false;
bool tess_domain_origin_lower_left = false;
bool multiview = false;
bool multiview_layered_rendering = true;
bool view_index_from_device_index = false;
bool dispatch_base = false;
bool texture_1D_as_2D = false;

View File

@ -232,6 +232,8 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths):
msl_args.append('--emit-line-directives')
if '.multiview.' in shader:
msl_args.append('--msl-multiview')
if '.no-layered.' in shader:
msl_args.append('--msl-multiview-no-layered-rendering')
if '.viewfromdev.' in shader:
msl_args.append('--msl-view-index-from-device-index')
if '.dispatchbase.' in shader: