Updated spirv-cross.

This commit is contained in:
Бранимир Караџић
2021-03-10 21:33:33 -08:00
parent 5f436c6cd6
commit 396717efb8
8 changed files with 496 additions and 102 deletions

View File

@@ -302,8 +302,20 @@ struct Instruction
{
uint16_t op = 0;
uint16_t count = 0;
// If offset is 0 (not a valid offset into the instruction stream),
// we have an instruction stream which is embedded in the object.
uint32_t offset = 0;
uint32_t length = 0;
inline bool is_embedded() const
{
return offset == 0;
}
};
struct EmbeddedInstruction : Instruction
{
SmallVector<uint32_t> ops;
};
enum Types

View File

@@ -513,9 +513,18 @@ protected:
if (!instr.length)
return nullptr;
if (instr.offset + instr.length > ir.spirv.size())
SPIRV_CROSS_THROW("Compiler::stream() out of range.");
return &ir.spirv[instr.offset];
if (instr.is_embedded())
{
auto &embedded = static_cast<const EmbeddedInstruction &>(instr);
assert(embedded.ops.size() == instr.length);
return embedded.ops.data();
}
else
{
if (instr.offset + instr.length > ir.spirv.size())
SPIRV_CROSS_THROW("Compiler::stream() out of range.");
return &ir.spirv[instr.offset];
}
}
ParsedIR ir;

View File

@@ -40,6 +40,13 @@ using namespace spv;
using namespace SPIRV_CROSS_NAMESPACE;
using namespace std;
enum ExtraSubExpressionType
{
// Create masks above any legal ID range to allow multiple address spaces into the extra_sub_expressions map.
EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET = 0x10000000,
EXTRA_SUB_EXPRESSION_TYPE_AUX = 0x20000000
};
static bool is_unsigned_opcode(Op op)
{
// Don't have to be exhaustive, only relevant for legacy target checking ...
@@ -3601,6 +3608,21 @@ void CompilerGLSL::emit_output_variable_initializer(const SPIRVariable &var)
statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];");
});
}
else if (has_decoration(var.self, DecorationBuiltIn) &&
BuiltIn(get_decoration(var.self, DecorationBuiltIn)) == BuiltInSampleMask)
{
// We cannot copy the array since gl_SampleMask is unsized in GLSL. Unroll time! <_<
entry_func.fixup_hooks_in.push_back([&] {
auto &c = this->get<SPIRConstant>(var.initializer);
uint32_t num_constants = uint32_t(c.subconstants.size());
for (uint32_t i = 0; i < num_constants; i++)
{
// Don't use to_expression on constant since it might be uint, just fish out the raw int.
statement(to_expression(var.self), "[", i, "] = ",
convert_to_string(this->get<SPIRConstant>(c.subconstants[i]).scalar_i32()), ";");
}
});
}
else
{
auto lut_name = join("_", var.self, "_init");
@@ -5700,14 +5722,27 @@ void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result
// Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends.
expected_type.basetype = input_type;
expected_type.width = expr_type.width;
string cast_op = expr_type.basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0);
string cast_op;
if (expr_type.basetype != input_type)
{
if (expr_type.basetype == SPIRType::Boolean)
cast_op = join(type_to_glsl(expected_type), "(", to_unpacked_expression(op0), ")");
else
cast_op = bitcast_glsl(expected_type, op0);
}
else
cast_op = to_unpacked_expression(op0);
string expr;
if (out_type.basetype != expected_result_type)
{
expected_type.basetype = expected_result_type;
expected_type.width = out_type.width;
expr = bitcast_glsl_op(out_type, expected_type);
if (out_type.basetype == SPIRType::Boolean)
expr = type_to_glsl(out_type);
else
expr = bitcast_glsl_op(out_type, expected_type);
expr += '(';
expr += join(op, "(", cast_op, ")");
expr += ')';
@@ -7364,7 +7399,7 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
{
// Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op.
// IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags.
uint32_t &max_id = extra_sub_expressions[id | 0x80000000u];
uint32_t &max_id = extra_sub_expressions[id | EXTRA_SUB_EXPRESSION_TYPE_AUX];
if (!max_id)
max_id = ir.increase_bound_by(1);
@@ -7646,6 +7681,9 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i)
case OpGroupNonUniformBitwiseAnd:
case OpGroupNonUniformBitwiseOr:
case OpGroupNonUniformBitwiseXor:
case OpGroupNonUniformLogicalAnd:
case OpGroupNonUniformLogicalOr:
case OpGroupNonUniformLogicalXor:
{
auto operation = static_cast<GroupOperation>(ops[3]);
if (operation == GroupOperationClusteredReduce)
@@ -7802,6 +7840,9 @@ case OpGroupNonUniform##op: \
GLSL_GROUP_OP(BitwiseAnd, And)
GLSL_GROUP_OP(BitwiseOr, Or)
GLSL_GROUP_OP(BitwiseXor, Xor)
GLSL_GROUP_OP(LogicalAnd, And)
GLSL_GROUP_OP(LogicalOr, Or)
GLSL_GROUP_OP(LogicalXor, Xor)
#undef GLSL_GROUP_OP
#undef GLSL_GROUP_OP_CAST
// clang-format on
@@ -9638,17 +9679,20 @@ void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_ex
{
handle_store_to_invariant_variable(lhs_expression, rhs_expression);
auto lhs = to_dereferenced_expression(lhs_expression);
if (!unroll_array_to_complex_store(lhs_expression, rhs_expression))
{
auto lhs = to_dereferenced_expression(lhs_expression);
// We might need to cast in order to store to a builtin.
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
// We might need to cast in order to store to a builtin.
cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression));
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
// While this is purely cosmetic, this is important for legacy ESSL where loop
// variable increments must be in either i++ or i += const-expr.
// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
statement(lhs, " = ", rhs, ";");
// Tries to optimize assignments like "<lhs> = <lhs> op expr".
// While this is purely cosmetic, this is important for legacy ESSL where loop
// variable increments must be in either i++ or i += const-expr.
// Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0.
if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs))
statement(lhs, " = ", rhs, ";");
}
register_write(lhs_expression);
}
}
@@ -9810,6 +9854,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// 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.
// Also, loading from gl_SampleMask array needs special unroll.
unroll_array_from_complex_load(id, ptr, expr);
// Shouldn't need to check for ID, but current glslang codegen requires it in some cases
@@ -10314,10 +10359,27 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
uint32_t rhs = ops[1];
if (lhs != rhs)
{
flush_variable_declaration(lhs);
flush_variable_declaration(rhs);
statement(to_expression(lhs), " = ", to_unpacked_expression(rhs), ";");
register_write(lhs);
uint32_t &tmp_id = extra_sub_expressions[instruction.offset | EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET];
if (!tmp_id)
tmp_id = ir.increase_bound_by(1);
uint32_t tmp_type_id = expression_type(rhs).parent_type;
EmbeddedInstruction fake_load, fake_store;
fake_load.op = OpLoad;
fake_load.length = 3;
fake_load.ops.push_back(tmp_type_id);
fake_load.ops.push_back(tmp_id);
fake_load.ops.push_back(rhs);
fake_store.op = OpStore;
fake_store.length = 2;
fake_store.ops.push_back(lhs);
fake_store.ops.push_back(tmp_id);
// Load and Store do a *lot* of workarounds, and we'd like to reuse them as much as possible.
// Synthesize a fake Load and Store pair for CopyMemory.
emit_instruction(fake_load);
emit_instruction(fake_store);
}
break;
}
@@ -12148,6 +12210,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
case OpGroupNonUniformBitwiseAnd:
case OpGroupNonUniformBitwiseOr:
case OpGroupNonUniformBitwiseXor:
case OpGroupNonUniformLogicalAnd:
case OpGroupNonUniformLogicalOr:
case OpGroupNonUniformLogicalXor:
case OpGroupNonUniformQuadSwap:
case OpGroupNonUniformQuadBroadcast:
emit_subgroup_op(instruction);
@@ -12791,7 +12856,7 @@ string CompilerGLSL::variable_decl(const SPIRVariable &variable)
// Ignore the pointer type since GLSL doesn't have pointers.
auto &type = get_variable_data_type(variable);
if (type.pointer_depth > 1)
if (type.pointer_depth > 1 && !backend.support_pointer_to_pointer)
SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types.");
auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self));
@@ -14920,6 +14985,43 @@ void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCl
statement(lhs, " = ", to_expression(rhs_id), ";");
}
bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id)
{
if (!backend.force_gl_in_out_block)
return false;
// This path is only relevant for GL backends.
auto *var = maybe_get<SPIRVariable>(target_id);
if (!var || var->storage != StorageClassOutput)
return false;
if (!is_builtin_variable(*var) || BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSampleMask)
return false;
auto &type = expression_type(source_id);
string array_expr;
if (type.array_size_literal.back())
{
array_expr = convert_to_string(type.array.back());
if (type.array.back() == 0)
SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array.");
}
else
array_expr = to_expression(type.array.back());
SPIRType target_type;
target_type.basetype = SPIRType::Int;
statement("for (int i = 0; i < int(", array_expr, "); i++)");
begin_scope();
statement(to_expression(target_id), "[i] = ",
bitcast_expression(target_type, type.basetype, join(to_expression(source_id), "[i]")),
";");
end_scope();
return true;
}
void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr)
{
if (!backend.force_gl_in_out_block)
@@ -14930,7 +15032,7 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
if (!var)
return;
if (var->storage != StorageClassInput)
if (var->storage != StorageClassInput && var->storage != StorageClassOutput)
return;
auto &type = get_variable_data_type(*var);
@@ -14938,9 +15040,13 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
return;
auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn));
bool is_builtin = is_builtin_variable(*var) && (builtin == BuiltInPointSize || builtin == BuiltInPosition);
bool is_builtin = is_builtin_variable(*var) &&
(builtin == BuiltInPointSize ||
builtin == BuiltInPosition ||
builtin == BuiltInSampleMask);
bool is_tess = is_tessellation_shader();
bool is_patch = has_decoration(var->self, DecorationPatch);
bool is_sample_mask = is_builtin && builtin == BuiltInSampleMask;
// Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it.
// We must unroll the array load.
@@ -14964,8 +15070,14 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
// The array size might be a specialization constant, so use a for-loop instead.
statement("for (int i = 0; i < int(", array_expr, "); i++)");
begin_scope();
if (is_builtin)
if (is_builtin && !is_sample_mask)
statement(new_expr, "[i] = gl_in[i].", expr, ";");
else if (is_sample_mask)
{
SPIRType target_type;
target_type.basetype = SPIRType::Int;
statement(new_expr, "[i] = ", bitcast_expression(target_type, type.basetype, join(expr, "[i]")), ";");
}
else
statement(new_expr, "[i] = ", expr, "[i];");
end_scope();
@@ -14976,6 +15088,10 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
{
// We will handle array cases elsewhere.
if (!expr_type.array.empty())
return;
auto *var = maybe_get_backing_variable(source_id);
if (var)
source_id = var->self;
@@ -15003,6 +15119,7 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
case BuiltInDrawIndex:
case BuiltInFragStencilRefEXT:
case BuiltInInstanceCustomIndexNV:
case BuiltInSampleMask:
expected_type = SPIRType::Int;
break;
@@ -15028,6 +15145,10 @@ void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
{
auto *var = maybe_get_backing_variable(target_id);
if (var)
target_id = var->self;
// Only interested in standalone builtin variables.
if (!has_decoration(target_id, DecorationBuiltIn))
return;
@@ -15042,6 +15163,7 @@ void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr,
case BuiltInPrimitiveId:
case BuiltInViewportIndex:
case BuiltInFragStencilRefEXT:
case BuiltInSampleMask:
expected_type = SPIRType::Int;
break;

View File

@@ -568,6 +568,7 @@ protected:
bool support_case_fallthrough = true;
bool use_array_constructor = false;
bool needs_row_major_load_workaround = false;
bool support_pointer_to_pointer = false;
} backend;
void emit_struct(SPIRType &type);
@@ -879,6 +880,7 @@ protected:
virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);

View File

@@ -4635,12 +4635,8 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
break;
case OpGroupNonUniformAllEqual:
{
auto &type = get<SPIRType>(result_type);
emit_unary_func_op(result_type, id, ops[3],
type.basetype == SPIRType::Boolean ? "WaveActiveAllEqualBool" : "WaveActiveAllEqual");
emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllEqual");
break;
}
// clang-format off
#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \
@@ -4688,6 +4684,9 @@ case OpGroupNonUniform##op: \
HLSL_GROUP_OP(BitwiseAnd, BitAnd, false)
HLSL_GROUP_OP(BitwiseOr, BitOr, false)
HLSL_GROUP_OP(BitwiseXor, BitXor, false)
HLSL_GROUP_OP_CAST(LogicalAnd, BitAnd, uint_type)
HLSL_GROUP_OP_CAST(LogicalOr, BitOr, uint_type)
HLSL_GROUP_OP_CAST(LogicalXor, BitXor, uint_type)
#undef HLSL_GROUP_OP
#undef HLSL_GROUP_OP_CAST

View File

@@ -100,7 +100,16 @@ void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bo
bool CompilerMSL::is_msl_shader_input_used(uint32_t location)
{
return inputs_in_use.count(location) != 0;
return location_inputs_in_use.count(location) != 0;
}
uint32_t CompilerMSL::get_automatic_builtin_input_location(spv::BuiltIn builtin) const
{
auto itr = builtin_to_automatic_input_location.find(builtin);
if (itr == builtin_to_automatic_input_location.end())
return k_unknown_location;
else
return itr->second;
}
bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const
@@ -390,6 +399,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType vec4_type_ptr;
vec4_type_ptr = vec4_type;
vec4_type_ptr.pointer = true;
vec4_type_ptr.pointer_depth++;
vec4_type_ptr.parent_type = type_id;
vec4_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
@@ -411,6 +421,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -432,6 +443,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -454,6 +466,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -473,6 +486,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -532,6 +546,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr_out;
uint_type_ptr_out = get_uint_type();
uint_type_ptr_out.pointer = true;
uint_type_ptr_out.pointer_depth++;
uint_type_ptr_out.parent_type = get_uint_type_id();
uint_type_ptr_out.storage = StorageClassOutput;
auto &ptr_out_type = set<SPIRType>(type_ptr_out_id, uint_type_ptr_out);
@@ -563,6 +578,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -622,6 +638,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -643,6 +660,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
@@ -701,6 +719,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr_out;
uint_type_ptr_out = get_uint_type();
uint_type_ptr_out.pointer = true;
uint_type_ptr_out.pointer_depth++;
uint_type_ptr_out.parent_type = get_uint_type_id();
uint_type_ptr_out.storage = StorageClassOutput;
@@ -722,6 +741,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType uint_type_ptr;
uint_type_ptr = get_uint_type();
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = get_uint_type_id();
uint_type_ptr.storage = StorageClassInput;
@@ -743,6 +763,7 @@ void CompilerMSL::build_implicit_builtins()
uint32_t type_id = build_extended_vector_type(get_uint_type_id(), 3);
SPIRType uint_type_ptr = get<SPIRType>(type_id);
uint_type_ptr.pointer = true;
uint_type_ptr.pointer_depth++;
uint_type_ptr.parent_type = type_id;
uint_type_ptr.storage = StorageClassInput;
@@ -835,6 +856,7 @@ void CompilerMSL::build_implicit_builtins()
SPIRType vec4_type_ptr;
vec4_type_ptr = vec4_type;
vec4_type_ptr.pointer = true;
vec4_type_ptr.pointer_depth++;
vec4_type_ptr.parent_type = type_id;
vec4_type_ptr.storage = StorageClassOutput;
auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
@@ -910,7 +932,7 @@ uint32_t CompilerMSL::build_constant_uint_array_pointer()
// Create a buffer to hold extra data, including the swizzle constants.
SPIRType uint_type_pointer = get_uint_type();
uint_type_pointer.pointer = true;
uint_type_pointer.pointer_depth = 1;
uint_type_pointer.pointer_depth++;
uint_type_pointer.parent_type = get_uint_type_id();
uint_type_pointer.storage = StorageClassUniform;
set<SPIRType>(type_ptr_id, uint_type_pointer);
@@ -1269,6 +1291,7 @@ string CompilerMSL::compile()
backend.array_is_value_type = !msl_options.force_native_arrays;
// Arrays which are part of buffer objects are never considered to be native arrays.
backend.buffer_offset_array_is_value_type = false;
backend.support_pointer_to_pointer = true;
capture_output_to_buffer = msl_options.capture_output_to_buffer;
is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer;
@@ -1730,6 +1753,7 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
ptr.self = mbr_type_id;
ptr.storage = var.storage;
ptr.pointer = true;
ptr.pointer_depth++;
ptr.parent_type = mbr_type_id;
func.add_parameter(mbr_type_id, var_id, true);
@@ -1801,34 +1825,28 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
}
}
uint32_t CompilerMSL::type_to_location_count(const SPIRType &type) const
{
// In MSL, we cannot place structs in any context where we need locations.
assert(type.basetype != SPIRType::Struct);
uint32_t dim = 1;
for (uint32_t i = 0; i < type.array.size(); i++)
dim *= to_array_size_literal(type, i);
uint32_t count = dim * type.columns;
return count;
}
// 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, const SPIRType &type, StorageClass storage)
{
if (storage != StorageClassInput)
return;
if (is_array(type))
{
uint32_t dim = 1;
for (uint32_t i = 0; i < type.array.size(); i++)
dim *= to_array_size_literal(type, i);
for (uint32_t i = 0; i < dim; i++)
{
if (is_matrix(type))
{
for (uint32_t j = 0; j < type.columns; j++)
inputs_in_use.insert(location++);
}
else
inputs_in_use.insert(location++);
}
}
else if (is_matrix(type))
{
for (uint32_t i = 0; i < type.columns; i++)
inputs_in_use.insert(location + i);
}
else
inputs_in_use.insert(location);
uint32_t count = type_to_location_count(type);
for (uint32_t i = 0; i < count; i++)
location_inputs_in_use.insert(location + i);
}
uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
@@ -1872,6 +1890,7 @@ uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t comp
type->parent_type = new_type_id;
type->storage = old_type.storage;
type->pointer = true;
type->pointer_depth++;
new_type_id = ptr_type_id;
}
@@ -2726,7 +2745,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
// Force the variable to have the proper name.
set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction));
if (get_entry_point().flags.get(ExecutionModeTriangles))
if (get_execution_mode_bitset().get(ExecutionModeTriangles))
{
// Triangles are tricky, because we want only one member in the struct.
@@ -2749,6 +2768,10 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
// Give the member a name
set_member_name(ib_type.self, ib_mbr_idx, mbr_name);
// We cannot decorate both, but the important part is that
// it's marked as builtin so we can get automatic attribute assignment if needed.
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
// There is no qualified alias since we need to flatten the internal array on return.
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
@@ -2796,6 +2819,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
uint32_t ptr_type_id = ir.increase_bound_by(1);
auto &new_var_type = set<SPIRType>(ptr_type_id, get<SPIRType>(type_id));
new_var_type.pointer = true;
new_var_type.pointer_depth++;
new_var_type.storage = StorageClassInput;
new_var_type.parent_type = type_id;
var.basetype = ptr_type_id;
@@ -2811,6 +2835,8 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
string qual_var_name = ib_var_ref + "." + mbr_name;
ir.meta[var.self].decoration.qualified_alias = qual_var_name;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin);
if (get_decoration_bitset(var.self).get(DecorationLocation))
{
uint32_t locn = get_decoration(var.self, DecorationLocation);
@@ -3301,6 +3327,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
set<SPIRType>(array_type_id, type);
type.pointer = true;
type.pointer_depth++;
type.parent_type = array_type_id;
type.storage = storage;
auto &ptr_type = set<SPIRType>(ptr_type_id, type);
@@ -3356,6 +3383,7 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
auto &ib_ptr_type = set<SPIRType>(ib_ptr_type_id, ib_type);
ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
ib_ptr_type.pointer = true;
ib_ptr_type.pointer_depth++;
ib_ptr_type.storage =
storage == StorageClassInput ?
(msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) :
@@ -3421,6 +3449,7 @@ uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn buil
auto &ptr_type = set<SPIRType>(ptr_type_id);
ptr_type = base_type;
ptr_type.pointer = true;
ptr_type.pointer_depth++;
ptr_type.storage = type.storage;
ptr_type.parent_type = base_type_id;
return ptr_type_id;
@@ -6088,6 +6117,30 @@ void CompilerMSL::emit_custom_functions()
}
}
static string inject_top_level_storage_qualifier(const string &expr, const string &qualifier)
{
// Easier to do this through text munging since the qualifier does not exist in the type system at all,
// and plumbing in all that information is not very helpful.
size_t last_reference = expr.find_last_of('&');
size_t last_pointer = expr.find_last_of('*');
size_t last_significant = string::npos;
if (last_reference == string::npos)
last_significant = last_pointer;
else if (last_pointer == string::npos)
last_significant = last_reference;
else
last_significant = std::max(last_reference, last_pointer);
if (last_significant == string::npos)
return join(qualifier, " ", expr);
else
{
return join(expr.substr(0, last_significant + 1), " ",
qualifier, expr.substr(last_significant + 1, string::npos));
}
}
// Undefined global memory is not allowed in MSL.
// Declare constant and init to zeros. Use {}, as global constructors can break Metal.
void CompilerMSL::declare_undefined_values()
@@ -6099,7 +6152,10 @@ void CompilerMSL::declare_undefined_values()
if (type.basetype == SPIRType::Void)
return;
statement("constant ", variable_decl(type, to_name(undef.self), undef.self), " = {};");
statement(inject_top_level_storage_qualifier(
variable_decl(type, to_name(undef.self), undef.self),
"constant"),
" = {};");
emitted = true;
});
@@ -6127,7 +6183,8 @@ void CompilerMSL::declare_constant_arrays()
if (!type.array.empty() && (!fully_inlined || is_scalar(type) || is_vector(type)))
{
auto name = to_name(c.self);
statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
statement(inject_top_level_storage_qualifier(variable_decl(type, name), "constant"),
" = ", constant_expression(c), ";");
emitted = true;
}
});
@@ -7997,6 +8054,7 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
bool lhs_thread = (lhs_storage == StorageClassOutput || lhs_storage == StorageClassFunction ||
lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate);
bool rhs_thread = (rhs_storage == StorageClassInput || rhs_storage == StorageClassFunction ||
rhs_storage == StorageClassOutput ||
rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate);
// If threadgroup storage qualifiers are *not* used:
@@ -8080,6 +8138,14 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
}
}
uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const
{
if (get_execution_mode_bitset().get(ExecutionModeTriangles))
return builtin == BuiltInTessLevelInner ? 1 : 3;
else
return builtin == BuiltInTessLevelInner ? 2 : 4;
}
// Since MSL does not allow arrays to be copied via simple variable assignment,
// if the LHS and RHS represent an assignment of an entire array, it must be
// implemented by calling an array copy function.
@@ -8108,6 +8174,25 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
return true;
}
if (get_execution_model() == ExecutionModelTessellationControl &&
has_decoration(id_lhs, DecorationBuiltIn))
{
auto builtin = BuiltIn(get_decoration(id_lhs, DecorationBuiltIn));
// Need to manually unroll the array store.
if (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)
{
uint32_t array_size = get_physical_tess_level_array_size(builtin);
if (array_size == 1)
statement(to_expression(id_lhs), " = half(", to_expression(id_rhs), "[0]);");
else
{
for (uint32_t i = 0; i < array_size; i++)
statement(to_expression(id_lhs), "[", i, "] = half(", to_expression(id_rhs), "[", i, "]);");
}
return true;
}
}
// Ensure the LHS variable has been declared
auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
if (p_v_lhs)
@@ -10047,7 +10132,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
return "";
}
}
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@@ -10087,7 +10178,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
}
uint32_t comp;
uint32_t locn = get_ordered_member_location(type.self, index, &comp);
uint32_t locn = get_member_location(type.self, index, &comp);
if (locn != k_unknown_location)
{
if (comp != k_unknown_component)
@@ -10123,7 +10214,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
if (msl_options.multi_patch_workgroup)
return "";
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@@ -10156,7 +10253,13 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
// The special control point array must not be marked with an attribute.
if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
return "";
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn;
if (is_builtin)
locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index);
else
locn = get_member_location(type.self, index);
if (locn != k_unknown_location)
return string(" [[attribute(") + convert_to_string(locn) + ")]]";
}
@@ -10196,7 +10299,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
else
{
uint32_t comp;
uint32_t locn = get_ordered_member_location(type.self, index, &comp);
uint32_t locn = get_member_location(type.self, index, &comp);
if (locn != k_unknown_location)
{
// For user-defined attributes, this is fine. From Vulkan spec:
@@ -10298,7 +10401,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
return "";
}
}
uint32_t locn = get_ordered_member_location(type.self, index);
uint32_t locn = get_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 "";
@@ -10347,24 +10450,61 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
// If the location of the member has been explicitly set, that location is used. If not, this
// function assumes the members are ordered in their location order, and simply returns the
// index as the location.
uint32_t CompilerMSL::get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp)
uint32_t CompilerMSL::get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp) const
{
auto &m = ir.meta[type_id];
if (index < m.members.size())
if (comp)
{
auto &dec = m.members[index];
if (comp)
{
if (dec.decoration_flags.get(DecorationComponent))
*comp = dec.component;
else
*comp = k_unknown_component;
}
if (dec.decoration_flags.get(DecorationLocation))
return dec.location;
if (has_member_decoration(type_id, index, DecorationComponent))
*comp = get_member_decoration(type_id, index, DecorationComponent);
else
*comp = k_unknown_component;
}
return index;
if (has_member_decoration(type_id, index, DecorationLocation))
return get_member_decoration(type_id, index, DecorationLocation);
else
return k_unknown_location;
}
uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
uint32_t type_id, uint32_t index,
uint32_t *comp)
{
uint32_t loc = get_member_location(type_id, index, comp);
if (loc != k_unknown_location)
return loc;
if (comp)
*comp = k_unknown_component;
// Late allocation. Find a location which is unused by the application.
// This can happen for built-in inputs in tessellation which are mixed and matched with user inputs.
auto &mbr_type = get<SPIRType>(get<SPIRType>(type_id).member_types[index]);
uint32_t count = type_to_location_count(mbr_type);
// This should always be 1.
if (count != 1)
return k_unknown_location;
loc = 0;
while (location_inputs_in_use.count(loc) != 0)
loc++;
set_member_decoration(type_id, index, DecorationLocation, loc);
// Triangle tess level inputs are shared in one packed float4,
// mark both builtins as sharing one location.
if (get_execution_mode_bitset().get(ExecutionModeTriangles) &&
(builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
{
builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc;
builtin_to_automatic_input_location[BuiltInTessLevelOuter] = loc;
}
else
builtin_to_automatic_input_location[builtin] = loc;
mark_location_as_used_by_shader(loc, mbr_type, StorageClassInput);
return loc;
}
// Returns the type declaration for a function, including the
@@ -10508,8 +10648,10 @@ string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bo
}
if (!addr_space)
{
// No address space for plain values.
addr_space = type.pointer || (argument && type.basetype == SPIRType::ControlPointArray) ? "thread" : "";
}
return join(flags.get(DecorationVolatile) || flags.get(DecorationCoherent) ? "volatile " : "", addr_space);
}
@@ -11838,6 +11980,24 @@ bool CompilerMSL::type_is_msl_framebuffer_fetch(const SPIRType &type) const
msl_options.use_framebuffer_fetch_subpasses;
}
bool CompilerMSL::type_is_pointer(const SPIRType &type) const
{
if (!type.pointer)
return false;
auto &parent_type = get<SPIRType>(type.parent_type);
// Safeguards when we forget to set pointer_depth (there is an assert for it in type_to_glsl),
// but the extra check shouldn't hurt.
return (type.pointer_depth > parent_type.pointer_depth) || !parent_type.pointer;
}
bool CompilerMSL::type_is_pointer_to_pointer(const SPIRType &type) const
{
if (!type.pointer)
return false;
auto &parent_type = get<SPIRType>(type.parent_type);
return type.pointer_depth > parent_type.pointer_depth && type_is_pointer(parent_type);
}
string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
{
auto &var = get<SPIRVariable>(arg.id);
@@ -11864,9 +12024,8 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
if (!type.array.empty() && type_is_image)
constref = true;
const char *cv_qualifier = constref ? "const " : "";
string decl;
if (constref)
decl += "const ";
// If this is a combined image-sampler for a 2D image with floating-point type,
// we emitted the 'spvDynamicImageSampler' type, and this is *not* an alias parameter
@@ -11880,27 +12039,38 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
// Allow Metal to use the array<T> template to make arrays a value type
string address_space = get_argument_address_space(var);
bool builtin = is_builtin_variable(var);
auto builtin_type = BuiltIn(get_decoration(arg.id, DecorationBuiltIn));
is_using_builtin_array = builtin;
if (address_space == "threadgroup")
is_using_builtin_array = true;
if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id))
decl += type_to_glsl(type, arg.id);
decl = join(cv_qualifier, type_to_glsl(type, arg.id));
else if (builtin)
decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id);
decl = join(cv_qualifier, builtin_type_decl(builtin_type, arg.id));
else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
{
is_using_builtin_array = true;
decl += join(type_to_glsl(type, arg.id), "*");
decl += join(cv_qualifier, type_to_glsl(type, arg.id), "*");
}
else if (is_dynamic_img_sampler)
{
decl += join("spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">");
decl = join(cv_qualifier, "spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">");
// Mark the variable so that we can handle passing it to another function.
set_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler);
}
else
decl += type_to_glsl(type, arg.id);
{
// The type is a pointer type we need to emit cv_qualifier late.
if (type_is_pointer(type))
{
decl = type_to_glsl(type, arg.id);
if (*cv_qualifier != '\0')
decl += join(" ", cv_qualifier);
}
else
decl = join(cv_qualifier, type_to_glsl(type, arg.id));
}
bool opaque_handle = storage == StorageClassUniformConstant;
@@ -11968,16 +12138,37 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
}
}
decl += " (&";
const char *restrict_kw = to_restrict(name_id);
if (*restrict_kw)
// Special case, need to override the array size here if we're using tess level as an argument.
if (get_execution_model() == ExecutionModelTessellationControl && builtin &&
(builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter))
{
decl += " ";
decl += restrict_kw;
uint32_t array_size = get_physical_tess_level_array_size(builtin_type);
if (array_size == 1)
{
decl += " &";
decl += to_expression(name_id);
}
else
{
decl += " (&";
decl += to_expression(name_id);
decl += ")";
decl += join("[", array_size, "]");
}
}
else
{
decl += " (&";
const char *restrict_kw = to_restrict(name_id);
if (*restrict_kw)
{
decl += " ";
decl += restrict_kw;
}
decl += to_expression(name_id);
decl += ")";
decl += type_to_array_glsl(type);
}
decl += to_expression(name_id);
decl += ")";
decl += type_to_array_glsl(type);
}
else if (!opaque_handle && (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct))
{
@@ -11985,8 +12176,12 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
// for the reference has to go before the '&', but after the '*'.
if (!address_space.empty())
{
if (decl.back() == '*')
decl += join(" ", address_space, " ");
if (type_is_pointer(type))
{
if (*cv_qualifier == '\0')
decl += ' ';
decl += join(address_space, " ");
}
else
decl = join(address_space, " ", decl);
}
@@ -12434,8 +12629,22 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
// Pointer?
if (type.pointer)
{
assert(type.pointer_depth > 0);
const char *restrict_kw;
type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get<SPIRType>(type.parent_type), id));
auto type_address_space = get_type_address_space(type, id);
auto type_decl = type_to_glsl(get<SPIRType>(type.parent_type), id);
// Work around C pointer qualifier rules. If glsl_type is a pointer type as well
// we'll need to emit the address space to the right.
// We could always go this route, but it makes the code unnatural.
// Prefer emitting thread T *foo over T thread* foo since it's more readable,
// but we'll have to emit thread T * thread * T constant bar; for example.
if (type_is_pointer_to_pointer(type))
type_name = join(type_decl, " ", type_address_space, " ");
else
type_name = join(type_address_space, " ", type_decl);
switch (type.basetype)
{
@@ -14531,14 +14740,36 @@ void CompilerMSL::cast_from_builtin_load(uint32_t source_id, std::string &expr,
if (expected_type != expr_type.basetype)
{
if (expected_width != expr_type.width)
if (!expr_type.array.empty() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
{
// These are of different widths, so we cannot do a straight bitcast.
expr = join(type_to_glsl(expr_type), "(", expr, ")");
// Triggers when loading TessLevel directly as an array.
// Need explicit padding + cast.
auto wrap_expr = join(type_to_glsl(expr_type), "({ ");
uint32_t array_size = get_physical_tess_level_array_size(builtin);
for (uint32_t i = 0; i < array_size; i++)
{
if (array_size > 1)
wrap_expr += join("float(", expr, "[", i, "])");
else
wrap_expr += join("float(", expr, ")");
if (i + 1 < array_size)
wrap_expr += ", ";
}
if (get_execution_mode_bitset().get(ExecutionModeTriangles))
wrap_expr += ", 0.0";
wrap_expr += " })";
expr = std::move(wrap_expr);
}
else
{
expr = bitcast_expression(expr_type, expected_type, expr);
// These are of different widths, so we cannot do a straight bitcast.
if (expected_width != expr_type.width)
expr = join(type_to_glsl(expr_type), "(", expr, ")");
else
expr = bitcast_expression(expr_type, expected_type, expr);
}
}
@@ -14776,7 +15007,7 @@ void CompilerMSL::analyze_argument_buffers()
// Create a buffer to hold extra data, including the swizzle constants.
SPIRType uint_type_pointer = get_uint_type();
uint_type_pointer.pointer = true;
uint_type_pointer.pointer_depth = 1;
uint_type_pointer.pointer_depth++;
uint_type_pointer.parent_type = get_uint_type_id();
uint_type_pointer.storage = StorageClassUniform;
set<SPIRType>(uint_ptr_type_id, uint_type_pointer);
@@ -14850,7 +15081,7 @@ void CompilerMSL::analyze_argument_buffers()
auto &ptr_type = set<SPIRType>(ptr_type_id);
ptr_type = buffer_type;
ptr_type.pointer = true;
ptr_type.pointer_depth = 1;
ptr_type.pointer_depth++;
ptr_type.parent_type = type_id;
uint32_t buffer_variable_id = next_id;
@@ -14937,6 +15168,7 @@ void CompilerMSL::analyze_argument_buffers()
set<SPIRType>(atomic_type_id, atomic_type);
atomic_type.pointer = true;
atomic_type.pointer_depth++;
atomic_type.parent_type = atomic_type_id;
atomic_type.storage = StorageClassStorageBuffer;
auto &atomic_ptr_type = set<SPIRType>(type_ptr_id, atomic_type);

View File

@@ -550,6 +550,13 @@ public:
// 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);
// If not using add_msl_shader_input, it's possible
// that certain builtin attributes need to be automatically assigned locations.
// This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc.
// This returns k_unknown_location if the location was explicitly assigned with
// add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]].
uint32_t get_automatic_builtin_input_location(spv::BuiltIn builtin) const;
// 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
@@ -567,6 +574,7 @@ public:
// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the
// sampler's binding is returned instead. For any other resource type, -1 is returned.
// Secondary bindings are also used for the auxillary image atomic buffer.
uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const;
// Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images,
@@ -825,7 +833,11 @@ protected:
std::string argument_decl(const SPIRFunction::Parameter &arg);
std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const;
uint32_t get_or_allocate_builtin_input_member_location(spv::BuiltIn builtin,
uint32_t type_id, uint32_t index, uint32_t *comp = nullptr);
uint32_t get_physical_tess_level_array_size(spv::BuiltIn builtin) const;
// MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output.
// These values can change depending on various extended decorations which control packing rules.
@@ -928,13 +940,15 @@ protected:
// Must be ordered to ensure declarations are in a specific order.
std::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_set<uint32_t> location_inputs_in_use;
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
std::set<std::string> pragma_lines;
std::set<std::string> typedef_lines;
SmallVector<uint32_t> vars_needing_early_declaration;
std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
uint32_t type_to_location_count(const SPIRType &type) const;
uint32_t next_metal_resource_index_buffer = 0;
uint32_t next_metal_resource_index_texture = 0;
@@ -1026,6 +1040,8 @@ protected:
void activate_argument_buffer_resources();
bool type_is_msl_framebuffer_fetch(const SPIRType &type) const;
bool type_is_pointer(const SPIRType &type) const;
bool type_is_pointer_to_pointer(const SPIRType &type) const;
bool is_supported_argument_buffer_type(const SPIRType &type) const;
// OpcodeHandler that handles several MSL preprocessing operations.

View File

@@ -140,6 +140,8 @@ void Parser::parse()
SPIRV_CROSS_THROW("Function was not terminated.");
if (current_block)
SPIRV_CROSS_THROW("Block was not terminated.");
if (ir.default_entry_point == 0)
SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module.");
}
const uint32_t *Parser::stream(const Instruction &instr) const