mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-18 04:53:06 +01:00
Updated spirv-cross.
This commit is contained in:
12
3rdparty/spirv-cross/spirv_common.hpp
vendored
12
3rdparty/spirv-cross/spirv_common.hpp
vendored
@@ -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
|
||||
|
||||
15
3rdparty/spirv-cross/spirv_cross.hpp
vendored
15
3rdparty/spirv-cross/spirv_cross.hpp
vendored
@@ -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;
|
||||
|
||||
162
3rdparty/spirv-cross/spirv_glsl.cpp
vendored
162
3rdparty/spirv-cross/spirv_glsl.cpp
vendored
@@ -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;
|
||||
|
||||
|
||||
2
3rdparty/spirv-cross/spirv_glsl.hpp
vendored
2
3rdparty/spirv-cross/spirv_glsl.hpp
vendored
@@ -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);
|
||||
|
||||
9
3rdparty/spirv-cross/spirv_hlsl.cpp
vendored
9
3rdparty/spirv-cross/spirv_hlsl.cpp
vendored
@@ -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
|
||||
|
||||
376
3rdparty/spirv-cross/spirv_msl.cpp
vendored
376
3rdparty/spirv-cross/spirv_msl.cpp
vendored
@@ -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);
|
||||
|
||||
20
3rdparty/spirv-cross/spirv_msl.hpp
vendored
20
3rdparty/spirv-cross/spirv_msl.hpp
vendored
@@ -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.
|
||||
|
||||
2
3rdparty/spirv-cross/spirv_parser.cpp
vendored
2
3rdparty/spirv-cross/spirv_parser.cpp
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user