MSL: Fix up input variables' vector lengths in all stages.

Metal is picky about interface matching. If the types don't match
exactly, down to the number of vector components, Metal fails pipline
compilation. To support pipelines where the number of components
consumed by the fragment shader is less than that produced by the vertex
shader, we have to fix up the fragment shader to accept all the
components produced.
This commit is contained in:
Chip Davis 2020-06-13 23:03:30 -05:00
parent 9e3df69d4e
commit 5281d9997e
14 changed files with 550 additions and 111 deletions

View File

@ -99,7 +99,7 @@ if (WIN32)
endif()
if (CMAKE_COMPILER_IS_GNUCXX OR ((${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") AND NOT MSVC))
set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Wshadow)
set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Wshadow -Wno-deprecated-declarations)
if (SPIRV_CROSS_MISC_WARNINGS)
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
set(spirv-compiler-options ${spirv-compiler-options} -Wshorten-64-to-32)
@ -323,7 +323,7 @@ if (SPIRV_CROSS_STATIC)
endif()
set(spirv-cross-abi-major 0)
set(spirv-cross-abi-minor 34)
set(spirv-cross-abi-minor 35)
set(spirv-cross-abi-patch 0)
if (SPIRV_CROSS_SHARED)

View File

@ -569,6 +569,7 @@ struct CLIArguments
SmallVector<uint32_t> msl_device_argument_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_dynamic_buffers;
SmallVector<pair<uint32_t, uint32_t>> msl_inline_uniform_blocks;
SmallVector<MSLShaderInput> msl_shader_inputs;
SmallVector<PLSArg> pls_in;
SmallVector<PLSArg> pls_out;
SmallVector<Remap> remaps;
@ -738,7 +739,10 @@ static void print_help_msl()
"\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n"
"\t[--msl-enable-frag-output-mask <mask>]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n"
"\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n"
);
"\t[--msl-shader-input <index> <format> <size>]:\n\t\tSpecify the format of the shader input at <index>.\n"
"\t\t<format> can be 'u16', 'u8', or 'other', to indicate a 16-bit unsigned integer, 8-bit unsigned integer, "
"or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
"\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n");
}
static void print_help_common()
@ -975,6 +979,8 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
msl_comp->add_dynamic_buffer(v.first, v.second, i++);
for (auto &v : args.msl_inline_uniform_blocks)
msl_comp->add_inline_uniform_block(v.first, v.second);
for (auto &v : args.msl_shader_inputs)
msl_comp->add_msl_shader_input(v);
}
else if (args.hlsl)
compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir())));
@ -1356,6 +1362,20 @@ static int main_inner(int argc, char *argv[])
[&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); });
cbs.add("--msl-no-clip-distance-user-varying",
[&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; });
cbs.add("--msl-shader-input", [&args](CLIParser &parser) {
MSLShaderInput input;
// Make sure next_uint() is called in-order.
input.location = parser.next_uint();
const char *format = parser.next_value_string("other");
if (strcmp(format, "u16") == 0)
input.format = MSL_VERTEX_FORMAT_UINT16;
else if (strcmp(format, "u8") == 0)
input.format = MSL_VERTEX_FORMAT_UINT8;
else
input.format = MSL_VERTEX_FORMAT_OTHER;
input.vecsize = parser.next_uint();
args.msl_shader_inputs.push_back(input);
});
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,75 @@
#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
{
ushort2 a [[user(locn0)]];
uint3 b [[user(locn1)]];
ushort c_0 [[user(locn2)]];
ushort c_1 [[user(locn3)]];
uint4 e_0 [[user(locn4)]];
uint4 e_1 [[user(locn5)]];
float4 d [[user(locn6)]];
};
fragment main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
spvUnsafeArray<ushort, 2> c = {};
spvUnsafeArray<uint4, 2> e = {};
c[0] = in.c_0;
c[1] = in.c_1;
e[0] = in.e_0;
e[1] = in.e_1;
out.FragColor = float4(float(int(in.a.x)), float(in.b.x), float2(float(uint(c[1])), float(e[0].w)) + in.d.xy);
return out;
}

View File

@ -0,0 +1,74 @@
#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 gl_Position [[position]];
};
struct main0_in
{
ushort2 a [[attribute(0)]];
uint3 b [[attribute(1)]];
ushort c_0 [[attribute(2)]];
ushort c_1 [[attribute(3)]];
uint4 d_0 [[attribute(4)]];
uint4 d_1 [[attribute(5)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
spvUnsafeArray<ushort, 2> c = {};
spvUnsafeArray<uint4, 2> d = {};
c[0] = in.c_0;
c[1] = in.c_1;
d[0] = in.d_0;
d[1] = in.d_1;
out.gl_Position = float4(float(int(in.a.x)), float(in.b.x), float(uint(c[1])), float(d[0].w));
return out;
}

View File

@ -0,0 +1,75 @@
#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
{
ushort2 a [[user(locn0)]];
uint3 b [[user(locn1)]];
ushort c_0 [[user(locn2)]];
ushort c_1 [[user(locn3)]];
uint4 e_0 [[user(locn4)]];
uint4 e_1 [[user(locn5)]];
float4 d [[user(locn6)]];
};
fragment main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
spvUnsafeArray<ushort, 2> c = {};
spvUnsafeArray<uint4, 2> e = {};
c[0] = in.c_0;
c[1] = in.c_1;
e[0] = in.e_0;
e[1] = in.e_1;
out.FragColor = float4(float(int(in.a.x)), float(in.b.x), float2(float(uint(c[1])), float(e[0].w)) + in.d.xy);
return out;
}

View File

@ -0,0 +1,74 @@
#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 gl_Position [[position]];
};
struct main0_in
{
ushort2 a [[attribute(0)]];
uint3 b [[attribute(1)]];
ushort c_0 [[attribute(2)]];
ushort c_1 [[attribute(3)]];
uint4 d_0 [[attribute(4)]];
uint4 d_1 [[attribute(5)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
spvUnsafeArray<ushort, 2> c = {};
spvUnsafeArray<uint4, 2> d = {};
c[0] = in.c_0;
c[1] = in.c_1;
d[0] = in.d_0;
d[1] = in.d_1;
out.gl_Position = float4(float(int(in.a.x)), float(in.b.x), float(uint(c[1])), float(d[0].w));
return out;
}

View File

@ -0,0 +1,17 @@
#version 450
#extension GL_AMD_gpu_shader_int16 : require
layout(location = 0) flat in int16_t a;
layout(location = 1) flat in ivec2 b;
layout(location = 2) flat in uint16_t c[2];
layout(location = 4) flat in uvec4 e[2];
layout(location = 6) in vec2 d;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vec4(float(int(a)), float(b.x), vec2(uint(c[1]), float(e[0].w)) + d);
}

View File

@ -0,0 +1,14 @@
#version 450
#extension GL_AMD_gpu_shader_int16 : require
layout(location = 0) in int16_t a;
layout(location = 1) in ivec2 b;
layout(location = 2) in uint16_t c[2];
layout(location = 4) in uvec4 d[2];
void main()
{
gl_Position = vec4(float(int(a)), float(b.x), float(uint(c[1])), float(d[0].w));
}

View File

@ -1032,6 +1032,30 @@ spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const
#endif
}
spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_input *si)
{
#if SPIRV_CROSS_C_API_MSL
if (compiler->backend != SPVC_BACKEND_MSL)
{
compiler->context->report_error("MSL function used on a non-MSL backend.");
return SPVC_ERROR_INVALID_ARGUMENT;
}
auto &msl = *static_cast<CompilerMSL *>(compiler->compiler.get());
MSLShaderInput input;
input.location = si->location;
input.format = static_cast<MSLShaderInputFormat>(si->format);
input.builtin = static_cast<spv::BuiltIn>(si->builtin);
input.vecsize = si->vecsize;
msl.add_msl_shader_input(input);
return SPVC_SUCCESS;
#else
(void)si;
compiler->context->report_error("MSL function used on a non-MSL backend.");
return SPVC_ERROR_INVALID_ARGUMENT;
#endif
}
spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
const spvc_msl_resource_binding *binding)
{
@ -2267,6 +2291,19 @@ void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr)
#endif
}
void spvc_msl_shader_input_init(spvc_msl_shader_input *input)
{
#if SPIRV_CROSS_C_API_MSL
MSLShaderInput input_default;
input->location = input_default.location;
input->format = static_cast<spvc_msl_shader_input_format>(input_default.format);
input->builtin = static_cast<SpvBuiltIn>(input_default.builtin);
input->vecsize = input_default.vecsize;
#else
memset(input, 0, sizeof(*input));
#endif
}
void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding)
{
#if SPIRV_CROSS_C_API_MSL

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 34
#define SPVC_C_API_VERSION_MINOR 35
/* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0
@ -259,14 +259,19 @@ typedef enum spvc_msl_platform
} spvc_msl_platform;
/* Maps to C++ API. */
typedef enum spvc_msl_vertex_format
typedef enum spvc_msl_shader_input_format
{
SPVC_MSL_VERTEX_FORMAT_OTHER = 0,
SPVC_MSL_VERTEX_FORMAT_UINT8 = 1,
SPVC_MSL_VERTEX_FORMAT_UINT16 = 2
} spvc_msl_vertex_format;
SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = 0,
SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
/* Maps to C++ API. */
/* Deprecated names. */
SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_INPUT_FORMAT_OTHER,
SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT8,
SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT16
} spvc_msl_shader_input_format, spvc_msl_vertex_format;
/* Maps to C++ API. Deprecated; use spvc_msl_shader_input. */
typedef struct spvc_msl_vertex_attribute
{
unsigned location;
@ -289,6 +294,20 @@ typedef struct spvc_msl_vertex_attribute
*/
SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
/* Maps to C++ API. */
typedef struct spvc_msl_shader_input
{
unsigned location;
spvc_msl_vertex_format format;
SpvBuiltIn builtin;
unsigned vecsize;
} spvc_msl_shader_input;
/*
* Initializes the shader input struct.
*/
SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input);
/* Maps to C++ API. */
typedef struct spvc_msl_resource_binding
{
@ -698,6 +717,8 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler
const spvc_msl_vertex_attribute *attrs);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
const spvc_msl_resource_binding *binding);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler,
const spvc_msl_shader_input *input);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);

View File

@ -8697,15 +8697,23 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
expr = to_unpacked_expression(ptr);
}
auto &type = get<SPIRType>(result_type);
auto &expr_type = expression_type(ptr);
// If the expression has more vector components than the result type, insert
// a swizzle. This shouldn't happen normally on valid SPIR-V, but it might
// happen with e.g. the MSL backend replacing the type of an input variable.
if (expr_type.vecsize > type.vecsize)
expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0));
// We might need to bitcast in order to load from a builtin.
bitcast_from_builtin_load(ptr, expr, get<SPIRType>(result_type));
bitcast_from_builtin_load(ptr, expr, type);
// We might be trying to load a gl_Position[N], where we should be
// doing float4[](gl_in[i].gl_Position, ...) instead.
// Similar workarounds are required for input arrays in tessellation.
unroll_array_from_complex_load(id, ptr, expr);
auto &type = get<SPIRType>(result_type);
// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
// when loading Image/Sampler descriptors. It does not hurt to check ID as well.
if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ptr, DecorationNonUniformEXT))

View File

@ -49,11 +49,20 @@ CompilerMSL::CompilerMSL(ParsedIR &&ir_)
{
}
void CompilerMSL::add_msl_shader_input(const MSLShaderInput &si)
{
inputs_by_location[si.location] = si;
if (si.builtin != BuiltInMax && !inputs_by_builtin.count(si.builtin))
inputs_by_builtin[si.builtin] = si;
}
void CompilerMSL::add_msl_vertex_attribute(const MSLVertexAttr &va)
{
vtx_attrs_by_location[va.location] = va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = va;
MSLShaderInput si;
si.location = va.location;
si.format = va.format;
si.builtin = va.builtin;
add_msl_shader_input(si);
}
void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
@ -93,7 +102,12 @@ void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bo
bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
{
return vtx_attrs_in_use.count(location) != 0;
return is_msl_shader_input_used(location);
}
bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
{
return inputs_in_use.count(location) != 0;
}
bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
@ -1458,11 +1472,11 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
}
}
// If a vertex attribute exists at the location, it is marked as being used by this shader
// If a shader input exists at the location, it is marked as being used by this shader
void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
{
if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput))
vtx_attrs_in_use.insert(location);
if (storage == StorageClassInput)
inputs_in_use.insert(location);
}
uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
@ -1474,11 +1488,13 @@ uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t locat
return itr->second;
}
uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components)
uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype)
{
uint32_t new_type_id = ir.increase_bound_by(1);
auto &type = set<SPIRType>(new_type_id, get<SPIRType>(type_id));
type.vecsize = components;
if (basetype != SPIRType::Unknown)
type.basetype = basetype;
type.self = new_type_id;
type.parent_type = type_id;
type.pointer = false;
@ -1634,11 +1650,9 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
if (storage == StorageClassInput)
{
type_id = ensure_correct_attribute_type(var.basetype, locn,
location_meta ? location_meta->num_components : type.vecsize);
type_id = ensure_correct_input_type(var.basetype, locn, location_meta ? location_meta->num_components : 0);
if (!location_meta)
var.basetype = type_id;
@ -1650,9 +1664,9 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
uint32_t locn = inputs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1797,19 +1811,18 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation) + i;
if (storage == StorageClassInput &&
(get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
if (storage == StorageClassInput)
{
var.basetype = ensure_correct_attribute_type(var.basetype, locn);
uint32_t mbr_type_id = ensure_correct_attribute_type(usable_type->self, locn);
var.basetype = ensure_correct_input_type(var.basetype, locn);
uint32_t mbr_type_id = ensure_correct_input_type(usable_type->self, locn);
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
uint32_t locn = inputs_by_builtin[builtin].location + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1987,9 +2000,9 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
uint32_t locn = inputs_by_builtin[builtin].location + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -2114,9 +2127,9 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation))
{
uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
if (storage == StorageClassInput)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
mbr_type_id = ensure_correct_input_type(mbr_type_id, locn);
var_type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
@ -2128,20 +2141,20 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
// The block itself might have a location and in this case, all members of the block
// receive incrementing locations.
uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array);
if (storage == StorageClassInput && (get_execution_model() == ExecutionModelVertex || is_tessellation_shader()))
if (storage == StorageClassInput)
{
mbr_type_id = ensure_correct_attribute_type(mbr_type_id, locn);
mbr_type_id = ensure_correct_input_type(mbr_type_id, locn);
var_type.member_types[mbr_idx] = mbr_type_id;
ib_type.member_types[ib_mbr_idx] = mbr_type_id;
}
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin))
{
uint32_t locn = 0;
auto builtin_itr = vtx_attrs_by_builtin.find(builtin);
if (builtin_itr != end(vtx_attrs_by_builtin))
auto builtin_itr = inputs_by_builtin.find(builtin);
if (builtin_itr != end(inputs_by_builtin))
locn = builtin_itr->second.location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
@ -2222,9 +2235,9 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
else if (vtx_attrs_by_builtin.count(builtin))
else if (inputs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
uint32_t locn = inputs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
@ -2283,9 +2296,9 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
else if (vtx_attrs_by_builtin.count(builtin))
else if (inputs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
uint32_t locn = inputs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
@ -2488,8 +2501,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
// 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 (get_execution_model() == ExecutionModelFragment && 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 && ((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;
@ -2784,63 +2797,49 @@ uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn buil
return type_id;
}
// Ensure that the type is compatible with the vertex attribute.
// Ensure that the type is compatible with the shader input.
// If it is, simply return the given type ID.
// Otherwise, create a new type, and return its ID.
uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components)
uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components)
{
auto &type = get<SPIRType>(type_id);
auto p_va = vtx_attrs_by_location.find(location);
if (p_va == end(vtx_attrs_by_location))
auto p_va = inputs_by_location.find(location);
if (p_va == end(inputs_by_location))
{
if (num_components != 0 && type.vecsize != num_components)
if (num_components > type.vecsize)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
}
if (num_components == 0)
num_components = p_va->second.vecsize;
switch (p_va->second.format)
{
case MSL_VERTEX_FORMAT_UINT8:
case MSL_SHADER_INPUT_FORMAT_UINT8:
{
switch (type.basetype)
{
case SPIRType::UByte:
case SPIRType::UShort:
case SPIRType::UInt:
if (num_components != 0 && type.vecsize != num_components)
if (num_components > type.vecsize)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
case SPIRType::Short:
return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
SPIRType::UShort);
case SPIRType::Int:
break;
return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
SPIRType::UInt);
default:
SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
}
uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
uint32_t base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id);
base_type = type;
base_type.basetype = type.basetype == SPIRType::Short ? SPIRType::UShort : SPIRType::UInt;
base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer)
return base_type_id;
uint32_t ptr_type_id = next_id++;
auto &ptr_type = set<SPIRType>(ptr_type_id);
ptr_type = base_type;
ptr_type.pointer = true;
ptr_type.storage = type.storage;
ptr_type.parent_type = base_type_id;
return ptr_type_id;
}
case MSL_VERTEX_FORMAT_UINT16:
@ -2849,41 +2848,22 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
{
case SPIRType::UShort:
case SPIRType::UInt:
if (num_components != 0 && type.vecsize != num_components)
if (num_components > type.vecsize)
return build_extended_vector_type(type_id, num_components);
else
return type_id;
case SPIRType::Int:
break;
return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize,
SPIRType::UInt);
default:
SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader");
}
uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1);
uint32_t base_type_id = next_id++;
auto &base_type = set<SPIRType>(base_type_id);
base_type = type;
base_type.basetype = SPIRType::UInt;
base_type.pointer = false;
if (num_components != 0)
base_type.vecsize = num_components;
if (!type.pointer)
return base_type_id;
uint32_t ptr_type_id = next_id++;
auto &ptr_type = set<SPIRType>(ptr_type_id);
ptr_type = base_type;
ptr_type.pointer = true;
ptr_type.storage = type.storage;
ptr_type.parent_type = base_type_id;
return ptr_type_id;
}
default:
if (num_components != 0 && type.vecsize != num_components)
if (num_components > type.vecsize)
type_id = build_extended_vector_type(type_id, num_components);
break;
}

View File

@ -27,26 +27,46 @@
namespace SPIRV_CROSS_NAMESPACE
{
// Indicates the format of the vertex attribute. Currently limited to specifying
// if the attribute is an 8-bit unsigned integer, 16-bit unsigned integer, or
// Indicates the format of a shader input. Currently limited to specifying
// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or
// some other format.
enum MSLVertexFormat
enum MSLShaderInputFormat
{
MSL_VERTEX_FORMAT_OTHER = 0,
MSL_VERTEX_FORMAT_UINT8 = 1,
MSL_VERTEX_FORMAT_UINT16 = 2,
MSL_VERTEX_FORMAT_INT_MAX = 0x7fffffff
MSL_SHADER_INPUT_FORMAT_OTHER = 0,
MSL_SHADER_INPUT_FORMAT_UINT8 = 1,
MSL_SHADER_INPUT_FORMAT_UINT16 = 2,
// Deprecated aliases.
MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_INPUT_FORMAT_OTHER,
MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_INPUT_FORMAT_UINT8,
MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_INPUT_FORMAT_UINT16,
MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff
};
typedef SPIRV_CROSS_DEPRECATED("Use MSLShaderInputFormat.") MSLShaderInputFormat MSLVertexFormat;
// Defines MSL characteristics of a vertex attribute at a particular location.
// After compilation, it is possible to query whether or not this location was used.
struct MSLVertexAttr
struct SPIRV_CROSS_DEPRECATED("Use MSLShaderInput.") MSLVertexAttr
{
uint32_t location = 0;
MSLVertexFormat format = MSL_VERTEX_FORMAT_OTHER;
spv::BuiltIn builtin = spv::BuiltInMax;
};
// Defines MSL characteristics of an input variable at a particular location.
// After compilation, it is possible to query whether or not this location was used.
// If vecsize is nonzero, it must be greater than or equal to the vecsize declared in the shader,
// or behavior is undefined.
struct MSLShaderInput
{
uint32_t location = 0;
bool per_instance = false;
MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
spv::BuiltIn builtin = spv::BuiltInMax;
uint32_t vecsize = 0;
};
// Matches the binding index of a MSL resource for a binding within a descriptor set.
// Taken together, the stage, desc_set and binding combine to form a reference to a resource
// descriptor used in a particular shading stage.
@ -423,8 +443,14 @@ public:
// vertex content locations to MSL attributes. If vertex attributes are provided,
// is_msl_vertex_attribute_used() will return true after calling ::compile() if
// the location was used by the MSL code.
SPIRV_CROSS_DEPRECATED("Use add_msl_shader_input().")
void add_msl_vertex_attribute(const MSLVertexAttr &attr);
// input is a shader input description used to fix up shader input variables.
// If shader inputs are provided, is_msl_shader_input_used() will return true after
// calling ::compile() if the location was used by the MSL code.
void add_msl_shader_input(const MSLShaderInput &attr);
// resource is a resource binding to indicate the MSL buffer,
// texture or sampler index to use for a particular SPIR-V description set
// and binding. If resource bindings are provided,
@ -456,8 +482,12 @@ public:
void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage);
// Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
SPIRV_CROSS_DEPRECATED("Use is_msl_shader_input_used().")
bool is_msl_vertex_attribute_used(uint32_t location);
// Query after compilation is done. This allows you to check if an input location was used by the shader.
bool is_msl_shader_input_used(uint32_t location);
// NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here.
// Constexpr samplers are always assumed to be emitted.
// No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped
@ -686,7 +716,7 @@ protected:
void mark_location_as_used_by_shader(uint32_t location, spv::StorageClass storage);
uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
uint32_t ensure_correct_attribute_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
void emit_custom_templates();
void emit_custom_functions();
@ -797,9 +827,9 @@ protected:
Options msl_options;
std::set<SPVFuncImpl> spv_function_implementations;
std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_builtin;
std::unordered_set<uint32_t> vtx_attrs_in_use;
std::unordered_map<uint32_t, MSLShaderInput> inputs_by_location;
std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
std::unordered_set<uint32_t> inputs_in_use;
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
std::set<std::string> pragma_lines;
std::set<std::string> typedef_lines;
@ -881,7 +911,7 @@ protected:
bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
uint32_t get_target_components_for_fragment_location(uint32_t location) const;
uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components);
uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype = SPIRType::Unknown);
bool suppress_missing_prototypes = false;

View File

@ -266,6 +266,20 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths):
msl_args.append('0x000000ca')
if '.no-user-varying.' in shader:
msl_args.append('--msl-no-clip-distance-user-varying')
if '.shader-inputs.' in shader:
# Arbitrary for testing purposes.
msl_args.append('--msl-shader-input')
msl_args.append('0')
msl_args.append('u8')
msl_args.append('2')
msl_args.append('--msl-shader-input')
msl_args.append('1')
msl_args.append('u16')
msl_args.append('3')
msl_args.append('--msl-shader-input')
msl_args.append('6')
msl_args.append('other')
msl_args.append('4')
subprocess.check_call(msl_args)