Rework the way arrays are handled in Metal to remove the array copies as they are unnecessary from Metal 1.2. There were cases where copies were not being inserted and others appeared unncessary, using the template type should allow the 'metal' compiler to do the best possible optimisation. The changes are broken into three stages. 1. Allow Metal to use the array<T> template to make arrays a value type. 2. Force the use of C style array declaration for some cases which cannot be wrapped with a template. 3. Threadgroup arrays can't have a wrapper type. 4. Tweak the code to use unsafe_array in a few more places so that we can handle passing arrays of resources into the shader and then through shaders into sub-functions. 5. Handle packed matrix types inside arrays within structs. 6. Make sure that builtin arguments still retain their array qualifiers when used in leaf functions. 7. Fix declaration of array-of-array constants for Metal so we can use the array<T> template.
This commit is contained in:
parent
b5ad5d4e2f
commit
d50659af92
@ -3342,11 +3342,19 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c)
|
||||
{
|
||||
// Handles Arrays and structures.
|
||||
string res;
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
bool bTrailingBracket = false;
|
||||
if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct &&
|
||||
type.array.empty())
|
||||
{
|
||||
res = type_to_glsl_constructor(type) + "{ ";
|
||||
}
|
||||
else if (backend.use_initializer_list && backend.use_typed_initializer_list &&
|
||||
!type.array.empty())
|
||||
{
|
||||
res = type_to_glsl(type) + "({ ";
|
||||
bTrailingBracket = true;
|
||||
}
|
||||
else if (backend.use_initializer_list)
|
||||
{
|
||||
res = "{ ";
|
||||
@ -3369,6 +3377,10 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c)
|
||||
}
|
||||
|
||||
res += backend.use_initializer_list ? " }" : ")";
|
||||
if (bTrailingBracket)
|
||||
res += ")";
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
return res;
|
||||
}
|
||||
else if (c.columns() == 1)
|
||||
|
@ -337,11 +337,15 @@ protected:
|
||||
|
||||
Options options;
|
||||
|
||||
std::string type_to_array_glsl(const SPIRType &type);
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
virtual std::string type_to_array_glsl(const SPIRType &type);
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
std::string to_array_size(const SPIRType &type, uint32_t index);
|
||||
uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const;
|
||||
uint32_t to_array_size_literal(const SPIRType &type) const;
|
||||
std::string variable_decl(const SPIRVariable &variable);
|
||||
/* UE Change Begin: Threadgroup arrays can't have a wrapper type */
|
||||
virtual std::string variable_decl(const SPIRVariable &variable);
|
||||
/* UE Change End: Threadgroup arrays can't have a wrapper type */
|
||||
std::string variable_decl_function_local(SPIRVariable &variable);
|
||||
|
||||
void add_local_variable_name(uint32_t id);
|
||||
|
511
spirv_msl.cpp
511
spirv_msl.cpp
@ -789,15 +789,23 @@ void CompilerMSL::emit_entry_point_declarations()
|
||||
// Emit buffer arrays here.
|
||||
for (uint32_t array_id : buffer_arrays)
|
||||
{
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
const auto &var = get<SPIRVariable>(array_id);
|
||||
const auto &type = get_variable_data_type(var);
|
||||
auto new_type = type;
|
||||
new_type.array.clear();
|
||||
const auto &base_type = get<SPIRType>(var.basetype);
|
||||
new_type.storage = base_type.storage;
|
||||
string name = to_name(array_id);
|
||||
statement(get_argument_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(array_id), name, "[] =");
|
||||
statement("unsafe_array<" + get_argument_address_space(var) + " " + type_to_glsl(new_type) + "*," + convert_to_string(type.array[0]) + "> " + name + " =");
|
||||
begin_scope();
|
||||
for (uint32_t i = 0; i < type.array[0]; ++i)
|
||||
statement(name + "_" + convert_to_string(i) + ",");
|
||||
end_scope_decl();
|
||||
statement_no_indent("");
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
}
|
||||
// For some reason, without this, we end up emitting the arrays twice.
|
||||
buffer_arrays.clear();
|
||||
@ -832,7 +840,9 @@ string CompilerMSL::compile()
|
||||
backend.can_declare_arrays_inline = false;
|
||||
backend.can_return_array = false;
|
||||
backend.allow_truncated_access_chain = true;
|
||||
backend.array_is_value_type = false;
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
backend.array_is_value_type = true;
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
backend.comparison_image_samples_scalar = true;
|
||||
backend.native_pointers = true;
|
||||
backend.nonuniform_qualifier = "";
|
||||
@ -946,11 +956,76 @@ void CompilerMSL::preprocess_op_codes()
|
||||
|
||||
suppress_missing_prototypes = preproc.suppress_missing_prototypes;
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-braces\"");
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
add_pragma_line("#pragma clang diagnostic ignored \"-Wunused-variable\"");
|
||||
|
||||
if (preproc.uses_atomics)
|
||||
{
|
||||
add_header_line("#include <metal_atomic>");
|
||||
add_pragma_line("#pragma clang diagnostic ignored \"-Wunused-variable\"");
|
||||
}
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
// UnsafeArray
|
||||
{
|
||||
add_header_line(" ");
|
||||
add_header_line("template <typename T, size_t Num>");
|
||||
add_header_line("struct unsafe_array");
|
||||
add_header_line("{");
|
||||
add_header_line(" T __Elements[Num ? Num : 1];");
|
||||
add_header_line(" ");
|
||||
add_header_line(" constexpr size_t size() const thread { return Num; }");
|
||||
add_header_line(" constexpr size_t max_size() const thread { return Num; }");
|
||||
add_header_line(" constexpr bool empty() const thread { return Num == 0; }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" constexpr size_t size() const device { return Num; }");
|
||||
add_header_line(" constexpr size_t max_size() const device { return Num; }");
|
||||
add_header_line(" constexpr bool empty() const device { return Num == 0; }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" constexpr size_t size() const constant { return Num; }");
|
||||
add_header_line(" constexpr size_t max_size() const constant { return Num; }");
|
||||
add_header_line(" constexpr bool empty() const constant { return Num == 0; }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" constexpr size_t size() const threadgroup { return Num; }");
|
||||
add_header_line(" constexpr size_t max_size() const threadgroup { return Num; }");
|
||||
add_header_line(" constexpr bool empty() const threadgroup { return Num == 0; }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" thread T &operator[](size_t pos) thread");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" constexpr const thread T &operator[](size_t pos) const thread");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" device T &operator[](size_t pos) device");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" constexpr const device T &operator[](size_t pos) const device");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" constexpr const constant T &operator[](size_t pos) const constant");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" ");
|
||||
add_header_line(" threadgroup T &operator[](size_t pos) threadgroup");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line(" constexpr const threadgroup T &operator[](size_t pos) const threadgroup");
|
||||
add_header_line(" {");
|
||||
add_header_line(" return __Elements[pos];");
|
||||
add_header_line(" }");
|
||||
add_header_line("};");
|
||||
}
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
// Metal vertex functions that write to resources must disable rasterization and return void.
|
||||
if (preproc.uses_resource_write)
|
||||
@ -3291,10 +3366,12 @@ void CompilerMSL::emit_custom_functions()
|
||||
end_scope();
|
||||
statement("");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
case SPVFuncImplTexelBufferCoords:
|
||||
{
|
||||
string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width);
|
||||
@ -4421,7 +4498,7 @@ void CompilerMSL::declare_undefined_values()
|
||||
bool emitted = false;
|
||||
ir.for_each_typed_id<SPIRUndef>([&](uint32_t, SPIRUndef &undef) {
|
||||
auto &type = this->get<SPIRType>(undef.basetype);
|
||||
statement("constant ", variable_decl(type, to_name(undef.self), undef.self), " = {};");
|
||||
statement("constant ", CompilerGLSL::variable_decl(type, to_name(undef.self), undef.self), " = {};");
|
||||
emitted = true;
|
||||
});
|
||||
|
||||
@ -4443,7 +4520,7 @@ void CompilerMSL::declare_constant_arrays()
|
||||
if (!type.array.empty())
|
||||
{
|
||||
auto name = to_name(c.self);
|
||||
statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
|
||||
statement("constant ", CompilerGLSL::variable_decl(type, name), " = ", constant_expression(c), ";");
|
||||
emitted = true;
|
||||
}
|
||||
});
|
||||
@ -4554,7 +4631,7 @@ void CompilerMSL::emit_specialization_constants_and_structs()
|
||||
auto &c = id.get<SPIRConstantOp>();
|
||||
auto &type = get<SPIRType>(c.basetype);
|
||||
auto name = to_name(c.self);
|
||||
statement("constant ", variable_decl(type, name), " = ", constant_op_expression(c), ";");
|
||||
statement("constant ", CompilerGLSL::variable_decl(type, name), " = ", constant_op_expression(c), ";");
|
||||
emitted = true;
|
||||
}
|
||||
else if (id.get_type() == TypeType)
|
||||
@ -4680,7 +4757,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
|
||||
if (is_matrix(*type) || is_array(*type) || type->basetype == SPIRType::Struct)
|
||||
{
|
||||
std::string temp_name = join(to_name(var->self), "_", ops[1]);
|
||||
statement(variable_decl(*type, temp_name, var->self), ";");
|
||||
statement(CompilerGLSL::variable_decl(*type, temp_name, var->self), ";");
|
||||
// Set up the initializer for this temporary variable.
|
||||
indices.push_back(const_mbr_id);
|
||||
if (type->basetype == SPIRType::Struct)
|
||||
@ -5648,59 +5725,76 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin
|
||||
void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageClass lhs_storage,
|
||||
StorageClass rhs_storage)
|
||||
{
|
||||
// Assignment from an array initializer is fine.
|
||||
auto &type = expression_type(rhs_id);
|
||||
auto *var = maybe_get_backing_variable(rhs_id);
|
||||
|
||||
// Unfortunately, we cannot template on address space in MSL,
|
||||
// so explicit address space redirection it is ...
|
||||
bool is_constant = false;
|
||||
if (ir.ids[rhs_id].get_type() == TypeConstant)
|
||||
{
|
||||
is_constant = true;
|
||||
}
|
||||
else if (var && var->remapped_variable && var->statically_assigned &&
|
||||
ir.ids[var->static_expression].get_type() == TypeConstant)
|
||||
{
|
||||
is_constant = true;
|
||||
}
|
||||
|
||||
// For the case where we have OpLoad triggering an array copy,
|
||||
// we cannot easily detect this case ahead of time since it's
|
||||
// context dependent. We might have to force a recompile here
|
||||
// if this is the only use of array copies in our shader.
|
||||
if (type.array.size() > 1)
|
||||
{
|
||||
if (type.array.size() > SPVFuncImplArrayCopyMultidimMax)
|
||||
SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
|
||||
auto func = static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type.array.size());
|
||||
add_spv_func_and_recompile(func);
|
||||
}
|
||||
else
|
||||
add_spv_func_and_recompile(SPVFuncImplArrayCopy);
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
bool lhs_thread =
|
||||
lhs_storage == StorageClassFunction || lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate;
|
||||
lhs_storage == StorageClassFunction || lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate;
|
||||
bool rhs_thread =
|
||||
rhs_storage == StorageClassFunction || rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate;
|
||||
|
||||
const char *tag = nullptr;
|
||||
if (lhs_thread && is_constant)
|
||||
tag = "FromConstantToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && is_constant)
|
||||
tag = "FromConstantToThreadGroup";
|
||||
else if (lhs_thread && rhs_thread)
|
||||
tag = "FromStackToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && rhs_thread)
|
||||
tag = "FromStackToThreadGroup";
|
||||
else if (lhs_thread && rhs_storage == StorageClassWorkgroup)
|
||||
tag = "FromThreadGroupToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup)
|
||||
tag = "FromThreadGroupToThreadGroup";
|
||||
rhs_storage == StorageClassFunction || rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate;
|
||||
|
||||
// If threadgroup storage qualifiers are *not* used:
|
||||
// Avoid spvCopy* wrapper functions; Otherwise, unsafe_array<> template cannot be used with that storage qualifier.
|
||||
if (lhs_thread && rhs_thread)
|
||||
{
|
||||
statement(lhs, " = ", to_expression(rhs_id), ";");
|
||||
}
|
||||
else
|
||||
SPIRV_CROSS_THROW("Unknown storage class used for copying arrays.");
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
{
|
||||
// Assignment from an array initializer is fine.
|
||||
auto &type = expression_type(rhs_id);
|
||||
auto *var = maybe_get_backing_variable(rhs_id);
|
||||
|
||||
statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
|
||||
// Unfortunately, we cannot template on address space in MSL,
|
||||
// so explicit address space redirection it is ...
|
||||
bool is_constant = false;
|
||||
if (ir.ids[rhs_id].get_type() == TypeConstant)
|
||||
{
|
||||
is_constant = true;
|
||||
}
|
||||
else if (var && var->remapped_variable && var->statically_assigned &&
|
||||
ir.ids[var->static_expression].get_type() == TypeConstant)
|
||||
{
|
||||
is_constant = true;
|
||||
}
|
||||
|
||||
// For the case where we have OpLoad triggering an array copy,
|
||||
// we cannot easily detect this case ahead of time since it's
|
||||
// context dependent. We might have to force a recompile here
|
||||
// if this is the only use of array copies in our shader.
|
||||
if (type.array.size() > 1)
|
||||
{
|
||||
if (type.array.size() > SPVFuncImplArrayCopyMultidimMax)
|
||||
SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
|
||||
auto func = static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type.array.size());
|
||||
add_spv_func_and_recompile(func);
|
||||
}
|
||||
else
|
||||
add_spv_func_and_recompile(SPVFuncImplArrayCopy);
|
||||
|
||||
const char *tag = nullptr;
|
||||
if (lhs_thread && is_constant)
|
||||
tag = "FromConstantToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && is_constant)
|
||||
tag = "FromConstantToThreadGroup";
|
||||
else if (lhs_thread && rhs_thread)
|
||||
tag = "FromStackToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && rhs_thread)
|
||||
tag = "FromStackToThreadGroup";
|
||||
else if (lhs_thread && rhs_storage == StorageClassWorkgroup)
|
||||
tag = "FromThreadGroupToStack";
|
||||
else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup)
|
||||
tag = "FromThreadGroupToThreadGroup";
|
||||
else
|
||||
SPIRV_CROSS_THROW("Unknown storage class used for copying arrays.");
|
||||
|
||||
// Pass internal array of unsafe_array<> into wrapper functions
|
||||
if (lhs_thread)
|
||||
statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".__Elements, ", to_expression(rhs_id), ");");
|
||||
else if (rhs_thread)
|
||||
statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ".__Elements);");
|
||||
else
|
||||
statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
|
||||
}
|
||||
}
|
||||
|
||||
// Since MSL does not allow arrays to be copied via simple variable assignment,
|
||||
@ -7329,6 +7423,10 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_
|
||||
|
||||
// If this member is packed, mark it as so.
|
||||
string pack_pfx;
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
string packed_array_type;
|
||||
string unpacked_array_type;
|
||||
|
||||
uint32_t orig_id = 0;
|
||||
if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID))
|
||||
@ -7362,11 +7460,11 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_
|
||||
}
|
||||
string base_type = physical_type.width == 16 ? "half" : "float";
|
||||
string td_line = "typedef ";
|
||||
td_line += "packed_" + base_type + to_string(rows);
|
||||
td_line += " " + pack_pfx;
|
||||
// Use the actual matrix size here.
|
||||
td_line += base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize);
|
||||
td_line += "[" + to_string(cols) + "]";
|
||||
unpacked_array_type = base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize);
|
||||
packed_array_type = pack_pfx + base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize);
|
||||
td_line += base_type + to_string(cols) + "x" + to_string(rows);
|
||||
td_line += " ";
|
||||
td_line += packed_array_type;
|
||||
td_line += ";";
|
||||
add_typedef_line(td_line);
|
||||
}
|
||||
@ -7393,11 +7491,32 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_
|
||||
if (physical_type.basetype != SPIRType::Image && physical_type.basetype != SPIRType::Sampler &&
|
||||
physical_type.basetype != SPIRType::SampledImage)
|
||||
{
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
BuiltIn builtin = BuiltInMax;
|
||||
use_builtin_array = is_member_builtin(type, index, &builtin);
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
|
||||
array_type = type_to_array_glsl(physical_type);
|
||||
}
|
||||
|
||||
if (!use_builtin_array && packed_array_type.length() > 0 && unpacked_array_type.length() > 0)
|
||||
pack_pfx = "";
|
||||
|
||||
return join(pack_pfx, type_to_glsl(*declared_type, orig_id), " ", qualifier, to_member_name(type, index),
|
||||
string result = join(pack_pfx, type_to_glsl(*declared_type, orig_id), " ", qualifier, to_member_name(type, index),
|
||||
member_attribute_qualifier(type, index), array_type, ";");
|
||||
|
||||
if (!use_builtin_array && packed_array_type.length() > 0 && unpacked_array_type.length() > 0)
|
||||
{
|
||||
auto it = result.find(unpacked_array_type);
|
||||
if (it != std::string::npos)
|
||||
{
|
||||
result.replace(it, unpacked_array_type.length(), packed_array_type);
|
||||
}
|
||||
}
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
use_builtin_array = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Emit a structure member, padding and packing to maintain the correct memeber alignments.
|
||||
@ -8232,6 +8351,9 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args)
|
||||
if (array_size == 0)
|
||||
SPIRV_CROSS_THROW("Unsized arrays of buffers are not supported in MSL.");
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
use_builtin_array = true;
|
||||
buffer_arrays.push_back(var_id);
|
||||
for (uint32_t i = 0; i < array_size; ++i)
|
||||
{
|
||||
@ -8241,6 +8363,9 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args)
|
||||
r.name + "_" + convert_to_string(i);
|
||||
ep_args += " [[buffer(" + convert_to_string(r.index + i) + ")]]";
|
||||
}
|
||||
use_builtin_array = false;
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -8759,13 +8884,48 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
type.image.dim == Dim2D && type_is_floating_point(get<SPIRType>(type.image.type)) &&
|
||||
spv_function_implementations.count(SPVFuncImplDynamicImageSampler);
|
||||
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
string address_space = get_argument_address_space(var);
|
||||
bool builtin = is_builtin_variable(var);
|
||||
use_builtin_array = builtin;
|
||||
if (address_space == "threadgroup")
|
||||
use_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);
|
||||
else if (builtin)
|
||||
decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id);
|
||||
else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type))
|
||||
decl += join(type_to_glsl(type, arg.id), "*");
|
||||
{
|
||||
auto new_type = type;
|
||||
new_type.array.clear();
|
||||
const auto &base_type = get<SPIRType>(var.basetype);
|
||||
new_type.storage = base_type.storage;
|
||||
decl += join("unsafe_array<" + address_space + " " + type_to_glsl(new_type) + "*," + convert_to_string(type.array[0]) + ">");
|
||||
|
||||
address_space = "thread";
|
||||
if (msl_options.argument_buffers)
|
||||
{
|
||||
uint32_t desc_set = get_decoration(name_id, DecorationDescriptorSet);
|
||||
if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) &&
|
||||
descriptor_set_is_argument_buffer(desc_set))
|
||||
{
|
||||
// An awkward case where we need to emit *more* address space declarations (yay!).
|
||||
// An example is where we pass down an array of buffer pointers to leaf functions.
|
||||
// It's a constant array containing pointers to constants.
|
||||
// The pointer array is always constant however. E.g.
|
||||
// device SSBO * constant (&array)[N].
|
||||
// const device SSBO * constant (&array)[N].
|
||||
// constant SSBO * constant (&array)[N].
|
||||
// However, this only matters for argument buffers, since for MSL 1.0 style codegen,
|
||||
// we emit the buffer array on stack instead, and that seems to work just fine apparently.
|
||||
address_space = "constant";
|
||||
}
|
||||
}
|
||||
//>>>>>>> 6ecced4b... Rework the way arrays are handled in Metal to remove the array copies as they are unnecessary from Metal 1.2. There were cases where copies were not being inserted and others appeared unncessary, using the template type should allow the 'metal' compiler to do the best possible optimisation. The changes are broken into three stages. 1. Allow Metal to use the array<T> template to make arrays a value type. 2. Force the use of C style array declaration for some cases which cannot be wrapped with a template. 3. Threadgroup arrays can't have a wrapper type. 4. Tweak the code to use unsafe_array in a few more places so that we can handle passing arrays of resources into the shader and then through shaders into sub-functions. 5. Handle packed matrix types inside arrays within structs. 6. Make sure that builtin arguments still retain their array qualifiers when used in leaf functions. 7. Fix declaration of array-of-array constants for Metal so we can use the array<T> template.
|
||||
}
|
||||
else if (is_dynamic_img_sampler)
|
||||
{
|
||||
decl += join("spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">");
|
||||
@ -8777,8 +8937,6 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
|
||||
bool opaque_handle = storage == StorageClassUniformConstant;
|
||||
|
||||
string address_space = get_argument_address_space(var);
|
||||
|
||||
if (!builtin && !opaque_handle && !is_pointer &&
|
||||
(storage == StorageClassFunction || storage == StorageClassGeneric))
|
||||
{
|
||||
@ -8818,25 +8976,6 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
if (!address_space.empty())
|
||||
decl = join(address_space, " ", decl);
|
||||
|
||||
if (msl_options.argument_buffers)
|
||||
{
|
||||
uint32_t desc_set = get_decoration(name_id, DecorationDescriptorSet);
|
||||
if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) &&
|
||||
descriptor_set_is_argument_buffer(desc_set))
|
||||
{
|
||||
// An awkward case where we need to emit *more* address space declarations (yay!).
|
||||
// An example is where we pass down an array of buffer pointers to leaf functions.
|
||||
// It's a constant array containing pointers to constants.
|
||||
// The pointer array is always constant however. E.g.
|
||||
// device SSBO * constant (&array)[N].
|
||||
// const device SSBO * constant (&array)[N].
|
||||
// constant SSBO * constant (&array)[N].
|
||||
// However, this only matters for argument buffers, since for MSL 1.0 style codegen,
|
||||
// we emit the buffer array on stack instead, and that seems to work just fine apparently.
|
||||
decl += " constant";
|
||||
}
|
||||
}
|
||||
|
||||
decl += " (&";
|
||||
const char *restrict_kw = to_restrict(name_id);
|
||||
if (*restrict_kw)
|
||||
@ -8872,6 +9011,10 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
decl += to_expression(name_id);
|
||||
}
|
||||
|
||||
use_builtin_array = false;
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
@ -9241,7 +9384,30 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
||||
if (type.pointer)
|
||||
{
|
||||
const char *restrict_kw;
|
||||
type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get<SPIRType>(type.parent_type), id));
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
if (use_builtin_array || type.array.size() == 0)
|
||||
{
|
||||
type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get<SPIRType>(type.parent_type), id));
|
||||
}
|
||||
else
|
||||
{
|
||||
SPIRType new_type = get<SPIRType>(type.parent_type);
|
||||
new_type.pointer = true;
|
||||
new_type.array.clear();
|
||||
|
||||
type_name = "unsafe_array<";
|
||||
type_name += join(get_type_address_space(type, id), " ", type_to_glsl(new_type, id));
|
||||
for (auto i = uint32_t(type.array.size()); i; i--)
|
||||
{
|
||||
type_name += ",";
|
||||
type_name += to_array_size(type, i - 1);
|
||||
type_name += ">";
|
||||
}
|
||||
return type_name;
|
||||
}
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
switch (type.basetype)
|
||||
{
|
||||
case SPIRType::Image:
|
||||
@ -9267,7 +9433,10 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
||||
{
|
||||
case SPIRType::Struct:
|
||||
// Need OpName lookup here to get a "sensible" name for a struct.
|
||||
return to_name(type.self);
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
type_name = to_name(type.self);
|
||||
break;
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
|
||||
case SPIRType::Image:
|
||||
case SPIRType::SampledImage:
|
||||
@ -9340,9 +9509,99 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
||||
if (type.vecsize > 1)
|
||||
type_name += to_string(type.vecsize);
|
||||
|
||||
return type_name;
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
if (type.array.empty())
|
||||
{
|
||||
return type_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_builtin_array)
|
||||
{
|
||||
return type_name;
|
||||
}
|
||||
else if (options.flatten_multidimensional_arrays)
|
||||
{
|
||||
string res = "unsafe_array<";
|
||||
res += type_name;
|
||||
res += ",";
|
||||
for (auto i = uint32_t(type.array.size()); i; i--)
|
||||
{
|
||||
res += enclose_expression(to_array_size(type, i - 1));
|
||||
if (i > 1)
|
||||
res += " * ";
|
||||
}
|
||||
res += ">";
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (type.array.size() > 1)
|
||||
{
|
||||
if (!options.es && options.version < 430)
|
||||
require_extension_internal("GL_ARB_arrays_of_arrays");
|
||||
else if (options.es && options.version < 310)
|
||||
SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310. "
|
||||
"Try using --flatten-multidimensional-arrays or set "
|
||||
"options.flatten_multidimensional_arrays to true.");
|
||||
}
|
||||
|
||||
string res;
|
||||
string sizes;
|
||||
for (auto i = uint32_t(type.array.size()); i; i--)
|
||||
{
|
||||
res += "unsafe_array<";
|
||||
sizes += ",";
|
||||
sizes += to_array_size(type, i - 1);
|
||||
sizes += ">";
|
||||
}
|
||||
|
||||
res += type_name + sizes;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
}
|
||||
|
||||
string CompilerMSL::type_to_array_glsl(const SPIRType &type)
|
||||
{
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
switch (type.basetype)
|
||||
{
|
||||
case SPIRType::AtomicCounter:
|
||||
case SPIRType::ControlPointArray:
|
||||
{
|
||||
return CompilerGLSL::type_to_array_glsl(type);
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (use_builtin_array)
|
||||
return CompilerGLSL::type_to_array_glsl(type);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
}
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
}
|
||||
|
||||
/* UE Change Begin: Threadgroup arrays can't have a wrapper type */
|
||||
std::string CompilerMSL::variable_decl(const SPIRVariable &variable)
|
||||
{
|
||||
if (variable.storage == StorageClassWorkgroup)
|
||||
{
|
||||
use_builtin_array = true;
|
||||
}
|
||||
std::string expr = CompilerGLSL::variable_decl(variable);
|
||||
if (variable.storage == StorageClassWorkgroup)
|
||||
{
|
||||
use_builtin_array = false;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
/* UE Change End: Threadgroup arrays can't have a wrapper type */
|
||||
|
||||
std::string CompilerMSL::sampler_type(const SPIRType &type)
|
||||
{
|
||||
if (!type.array.empty())
|
||||
@ -10575,58 +10834,15 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
|
||||
|
||||
case OpFunctionCall:
|
||||
{
|
||||
auto &return_type = compiler.get<SPIRType>(args[0]);
|
||||
if (return_type.array.size() > 1)
|
||||
{
|
||||
if (return_type.array.size() > SPVFuncImplArrayCopyMultidimMax)
|
||||
SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
|
||||
return static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + return_type.array.size());
|
||||
}
|
||||
else if (return_type.array.size() > 0)
|
||||
return SPVFuncImplArrayCopy;
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
break;
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
{
|
||||
// Get the result type of the RHS. Since this is run as a pre-processing stage,
|
||||
// we must extract the result type directly from the Instruction, rather than the ID.
|
||||
uint32_t id_lhs = args[0];
|
||||
uint32_t id_rhs = args[1];
|
||||
|
||||
const SPIRType *type = nullptr;
|
||||
if (compiler.ir.ids[id_rhs].get_type() != TypeNone)
|
||||
{
|
||||
// Could be a constant, or similar.
|
||||
type = &compiler.expression_type(id_rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Or ... an expression.
|
||||
uint32_t tid = result_types[id_rhs];
|
||||
if (tid)
|
||||
type = &compiler.get<SPIRType>(tid);
|
||||
}
|
||||
|
||||
auto *var = compiler.maybe_get<SPIRVariable>(id_lhs);
|
||||
|
||||
// Are we simply assigning to a statically assigned variable which takes a constant?
|
||||
// Don't bother emitting this function.
|
||||
bool static_expression_lhs =
|
||||
var && var->storage == StorageClassFunction && var->statically_assigned && var->remapped_variable;
|
||||
if (type && compiler.is_array(*type) && !static_expression_lhs)
|
||||
{
|
||||
if (type->array.size() > 1)
|
||||
{
|
||||
if (type->array.size() > SPVFuncImplArrayCopyMultidimMax)
|
||||
SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
|
||||
return static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type->array.size());
|
||||
}
|
||||
else
|
||||
return SPVFuncImplArrayCopy;
|
||||
}
|
||||
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
break;
|
||||
}
|
||||
|
||||
@ -10638,6 +10854,31 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
|
||||
uint32_t tid = result_types[args[opcode == OpImageWrite ? 0 : 2]];
|
||||
if (tid && compiler.get<SPIRType>(tid).image.dim == DimBuffer && !compiler.msl_options.texture_buffer_native)
|
||||
return SPVFuncImplTexelBufferCoords;
|
||||
|
||||
if (opcode == OpImageFetch && compiler.msl_options.swizzle_texture_samples)
|
||||
return SPVFuncImplTextureSwizzle;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OpImageSampleExplicitLod:
|
||||
case OpImageSampleProjExplicitLod:
|
||||
case OpImageSampleDrefExplicitLod:
|
||||
case OpImageSampleProjDrefExplicitLod:
|
||||
case OpImageSampleImplicitLod:
|
||||
case OpImageSampleProjImplicitLod:
|
||||
case OpImageSampleDrefImplicitLod:
|
||||
case OpImageSampleProjDrefImplicitLod:
|
||||
case OpImageGather:
|
||||
case OpImageDrefGather:
|
||||
if (compiler.msl_options.swizzle_texture_samples)
|
||||
return SPVFuncImplTextureSwizzle;
|
||||
break;
|
||||
|
||||
case OpCompositeConstruct:
|
||||
{
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -537,6 +537,12 @@ protected:
|
||||
const std::string &qualifier = "", uint32_t base_offset = 0) override;
|
||||
void emit_struct_padding_target(const SPIRType &type) override;
|
||||
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
std::string type_to_array_glsl(const SPIRType &type) override;
|
||||
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
|
||||
/* UE Change Begin: Threadgroup arrays can't have a wrapper type */
|
||||
std::string variable_decl(const SPIRVariable &variable) override;
|
||||
/* UE Change End: Threadgroup arrays can't have a wrapper type */
|
||||
std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||
std::string sampler_type(const SPIRType &type);
|
||||
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
|
||||
@ -750,6 +756,9 @@ protected:
|
||||
bool has_sampled_images = false;
|
||||
bool needs_vertex_idx_arg = false;
|
||||
bool needs_instance_idx_arg = false;
|
||||
/* UE Change Begin: Force the use of C style array declaration. */
|
||||
bool use_builtin_array = false;
|
||||
/* UE Change End: Force the use of C style array declaration. */
|
||||
bool is_rasterization_disabled = false;
|
||||
bool capture_output_to_buffer = false;
|
||||
bool needs_swizzle_buffer_def = false;
|
||||
|
Loading…
Reference in New Issue
Block a user