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:
Mark Satterthwaite 2019-08-13 18:18:48 -04:00 committed by Lukas Hermanns
parent b5ad5d4e2f
commit d50659af92
4 changed files with 403 additions and 137 deletions

View File

@ -3342,11 +3342,19 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c)
{ {
// Handles Arrays and structures. // Handles Arrays and structures.
string res; 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 && if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct &&
type.array.empty()) type.array.empty())
{ {
res = type_to_glsl_constructor(type) + "{ "; 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) else if (backend.use_initializer_list)
{ {
res = "{ "; res = "{ ";
@ -3369,6 +3377,10 @@ string CompilerGLSL::constant_expression(const SPIRConstant &c)
} }
res += backend.use_initializer_list ? " }" : ")"; 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; return res;
} }
else if (c.columns() == 1) else if (c.columns() == 1)

View File

@ -337,11 +337,15 @@ protected:
Options options; 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); 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, uint32_t index) const;
uint32_t to_array_size_literal(const SPIRType &type) 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); std::string variable_decl_function_local(SPIRVariable &variable);
void add_local_variable_name(uint32_t id); void add_local_variable_name(uint32_t id);

View File

@ -789,15 +789,23 @@ void CompilerMSL::emit_entry_point_declarations()
// Emit buffer arrays here. // Emit buffer arrays here.
for (uint32_t array_id : buffer_arrays) 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 &var = get<SPIRVariable>(array_id);
const auto &type = get_variable_data_type(var); 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); 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(); begin_scope();
for (uint32_t i = 0; i < type.array[0]; ++i) for (uint32_t i = 0; i < type.array[0]; ++i)
statement(name + "_" + convert_to_string(i) + ","); statement(name + "_" + convert_to_string(i) + ",");
end_scope_decl(); end_scope_decl();
statement_no_indent(""); 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. // For some reason, without this, we end up emitting the arrays twice.
buffer_arrays.clear(); buffer_arrays.clear();
@ -832,7 +840,9 @@ string CompilerMSL::compile()
backend.can_declare_arrays_inline = false; backend.can_declare_arrays_inline = false;
backend.can_return_array = false; backend.can_return_array = false;
backend.allow_truncated_access_chain = true; 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.comparison_image_samples_scalar = true;
backend.native_pointers = true; backend.native_pointers = true;
backend.nonuniform_qualifier = ""; backend.nonuniform_qualifier = "";
@ -946,12 +956,77 @@ void CompilerMSL::preprocess_op_codes()
suppress_missing_prototypes = preproc.suppress_missing_prototypes; 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) if (preproc.uses_atomics)
{ {
add_header_line("#include <metal_atomic>"); 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. // Metal vertex functions that write to resources must disable rasterization and return void.
if (preproc.uses_resource_write) if (preproc.uses_resource_write)
is_rasterization_disabled = true; is_rasterization_disabled = true;
@ -3291,10 +3366,12 @@ void CompilerMSL::emit_custom_functions()
end_scope(); end_scope();
statement(""); statement("");
} }
break; 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: case SPVFuncImplTexelBufferCoords:
{ {
string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width); 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; bool emitted = false;
ir.for_each_typed_id<SPIRUndef>([&](uint32_t, SPIRUndef &undef) { ir.for_each_typed_id<SPIRUndef>([&](uint32_t, SPIRUndef &undef) {
auto &type = this->get<SPIRType>(undef.basetype); 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; emitted = true;
}); });
@ -4443,7 +4520,7 @@ void CompilerMSL::declare_constant_arrays()
if (!type.array.empty()) if (!type.array.empty())
{ {
auto name = to_name(c.self); 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; emitted = true;
} }
}); });
@ -4554,7 +4631,7 @@ void CompilerMSL::emit_specialization_constants_and_structs()
auto &c = id.get<SPIRConstantOp>(); auto &c = id.get<SPIRConstantOp>();
auto &type = get<SPIRType>(c.basetype); auto &type = get<SPIRType>(c.basetype);
auto name = to_name(c.self); 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; emitted = true;
} }
else if (id.get_type() == TypeType) 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) if (is_matrix(*type) || is_array(*type) || type->basetype == SPIRType::Struct)
{ {
std::string temp_name = join(to_name(var->self), "_", ops[1]); 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. // Set up the initializer for this temporary variable.
indices.push_back(const_mbr_id); indices.push_back(const_mbr_id);
if (type->basetype == SPIRType::Struct) if (type->basetype == SPIRType::Struct)
@ -5647,6 +5724,21 @@ 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, void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageClass lhs_storage,
StorageClass rhs_storage) StorageClass rhs_storage)
{
/* 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;
bool rhs_thread =
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
/* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
{ {
// Assignment from an array initializer is fine. // Assignment from an array initializer is fine.
auto &type = expression_type(rhs_id); auto &type = expression_type(rhs_id);
@ -5679,11 +5771,6 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
else else
add_spv_func_and_recompile(SPVFuncImplArrayCopy); add_spv_func_and_recompile(SPVFuncImplArrayCopy);
bool lhs_thread =
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; const char *tag = nullptr;
if (lhs_thread && is_constant) if (lhs_thread && is_constant)
tag = "FromConstantToStack"; tag = "FromConstantToStack";
@ -5700,8 +5787,15 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageCla
else else
SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); 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), ");"); statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
} }
}
// Since MSL does not allow arrays to be copied via simple variable assignment, // 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 // if the LHS and RHS represent an assignment of an entire array, it must be
@ -7330,6 +7424,10 @@ string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_
// If this member is packed, mark it as so. // If this member is packed, mark it as so.
string pack_pfx; 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; uint32_t orig_id = 0;
if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID)) if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID))
orig_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID); orig_id = get_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 base_type = physical_type.width == 16 ? "half" : "float";
string td_line = "typedef "; string td_line = "typedef ";
td_line += "packed_" + base_type + to_string(rows); unpacked_array_type = base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize);
td_line += " " + pack_pfx; packed_array_type = pack_pfx + base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize);
// Use the actual matrix size here. td_line += base_type + to_string(cols) + "x" + to_string(rows);
td_line += base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize); td_line += " ";
td_line += "[" + to_string(cols) + "]"; td_line += packed_array_type;
td_line += ";"; td_line += ";";
add_typedef_line(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 && if (physical_type.basetype != SPIRType::Image && physical_type.basetype != SPIRType::Sampler &&
physical_type.basetype != SPIRType::SampledImage) 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); array_type = type_to_array_glsl(physical_type);
} }
return join(pack_pfx, type_to_glsl(*declared_type, orig_id), " ", qualifier, to_member_name(type, index), if (!use_builtin_array && packed_array_type.length() > 0 && unpacked_array_type.length() > 0)
pack_pfx = "";
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, ";"); 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. // 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) if (array_size == 0)
SPIRV_CROSS_THROW("Unsized arrays of buffers are not supported in MSL."); 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); buffer_arrays.push_back(var_id);
for (uint32_t i = 0; i < array_size; ++i) 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); r.name + "_" + convert_to_string(i);
ep_args += " [[buffer(" + convert_to_string(r.index + 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 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)) && type.image.dim == Dim2D && type_is_floating_point(get<SPIRType>(type.image.type)) &&
spv_function_implementations.count(SPVFuncImplDynamicImageSampler); 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); 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)) 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 += type_to_glsl(type, arg.id);
else if (builtin) else if (builtin)
decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id); decl += builtin_type_decl(static_cast<BuiltIn>(get_decoration(arg.id, DecorationBuiltIn)), arg.id);
else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type)) 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) else if (is_dynamic_img_sampler)
{ {
decl += join("spvDynamicImageSampler<", type_to_glsl(get<SPIRType>(type.image.type)), ">"); 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; bool opaque_handle = storage == StorageClassUniformConstant;
string address_space = get_argument_address_space(var);
if (!builtin && !opaque_handle && !is_pointer && if (!builtin && !opaque_handle && !is_pointer &&
(storage == StorageClassFunction || storage == StorageClassGeneric)) (storage == StorageClassFunction || storage == StorageClassGeneric))
{ {
@ -8818,25 +8976,6 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
if (!address_space.empty()) if (!address_space.empty())
decl = join(address_space, " ", decl); 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 += " (&"; decl += " (&";
const char *restrict_kw = to_restrict(name_id); const char *restrict_kw = to_restrict(name_id);
if (*restrict_kw) if (*restrict_kw)
@ -8872,6 +9011,10 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
decl += to_expression(name_id); 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; return decl;
} }
@ -9241,7 +9384,30 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
if (type.pointer) if (type.pointer)
{ {
const char *restrict_kw; const char *restrict_kw;
/* 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)); 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) switch (type.basetype)
{ {
case SPIRType::Image: case SPIRType::Image:
@ -9267,7 +9433,10 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
{ {
case SPIRType::Struct: case SPIRType::Struct:
// Need OpName lookup here to get a "sensible" name for a 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::Image:
case SPIRType::SampledImage: case SPIRType::SampledImage:
@ -9340,8 +9509,98 @@ string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id)
if (type.vecsize > 1) if (type.vecsize > 1)
type_name += to_string(type.vecsize); type_name += to_string(type.vecsize);
/* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
if (type.array.empty())
{
return type_name; 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) std::string CompilerMSL::sampler_type(const SPIRType &type)
{ {
@ -10575,58 +10834,15 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
case OpFunctionCall: case OpFunctionCall:
{ {
auto &return_type = compiler.get<SPIRType>(args[0]); /* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
if (return_type.array.size() > 1) /* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
{
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;
break; break;
} }
case OpStore: case OpStore:
{ {
// Get the result type of the RHS. Since this is run as a pre-processing stage, /* UE Change Begin: Allow Metal to use the array<T> template to make arrays a value type */
// we must extract the result type directly from the Instruction, rather than the ID. /* UE Change End: Allow Metal to use the array<T> template to make arrays a value type */
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;
}
break; 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]]; 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) if (tid && compiler.get<SPIRType>(tid).image.dim == DimBuffer && !compiler.msl_options.texture_buffer_native)
return SPVFuncImplTexelBufferCoords; 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; break;
} }

View File

@ -537,6 +537,12 @@ protected:
const std::string &qualifier = "", uint32_t base_offset = 0) override; const std::string &qualifier = "", uint32_t base_offset = 0) override;
void emit_struct_padding_target(const SPIRType &type) override; void emit_struct_padding_target(const SPIRType &type) override;
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) 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 image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
std::string sampler_type(const SPIRType &type); std::string sampler_type(const SPIRType &type);
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
@ -750,6 +756,9 @@ protected:
bool has_sampled_images = false; bool has_sampled_images = false;
bool needs_vertex_idx_arg = false; bool needs_vertex_idx_arg = false;
bool needs_instance_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 is_rasterization_disabled = false;
bool capture_output_to_buffer = false; bool capture_output_to_buffer = false;
bool needs_swizzle_buffer_def = false; bool needs_swizzle_buffer_def = false;