MSL cannot declare inline arrays except in certain cases.

This commit is contained in:
Hans-Kristian Arntzen 2018-02-08 13:06:29 +01:00
parent 156dd905fd
commit 1a9c960058
9 changed files with 131 additions and 15 deletions

View File

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,12 @@ struct Foobar
float b; float b;
}; };
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
constant float4 _49[2] = {float4(1.0), float4(2.0)};
constant float4 _54[2] = {float4(8.0), float4(10.0)};
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
struct main0_in struct main0_in
{ {
int index [[user(locn0)]]; int index [[user(locn0)]];
@ -19,6 +27,13 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

View File

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,9 @@ struct Foo
float b; float b;
}; };
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
struct main0_in struct main0_in
{ {
int line [[user(locn0)]]; int line [[user(locn0)]];
@ -19,6 +24,13 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

View File

@ -3,6 +3,8 @@
using namespace metal; using namespace metal;
constant float4 _20[2] = {float4(10.0), float4(20.0)};
struct main0_in struct main0_in
{ {
float4 vInput1 [[attribute(1)]]; float4 vInput1 [[attribute(1)]];

View File

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,11 +11,20 @@ struct D
float b; float b;
}; };
constant float4 _14[4] = {float4(0.0), float4(0.0), float4(0.0), float4(0.0)};
struct main0_out struct main0_out
{ {
float FragColor [[color(0)]]; float FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0() fragment main0_out main0()
{ {
main0_out out = {}; main0_out out = {};

View File

@ -11,6 +11,12 @@ struct Foobar
float b; float b;
}; };
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
constant float4 _49[2] = {float4(1.0), float4(2.0)};
constant float4 _54[2] = {float4(8.0), float4(10.0)};
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
struct main0_in struct main0_in
{ {
int index [[user(locn0)]]; int index [[user(locn0)]];
@ -21,6 +27,13 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
float4 resolve(thread const Foobar& f) float4 resolve(thread const Foobar& f)
{ {
return float4(f.a + f.b); return float4(f.a + f.b);

View File

@ -1,3 +1,5 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib> #include <metal_stdlib>
#include <simd/simd.h> #include <simd/simd.h>
@ -9,6 +11,9 @@ struct Foo
float b; float b;
}; };
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
struct main0_in struct main0_in
{ {
int line [[user(locn0)]]; int line [[user(locn0)]];
@ -19,6 +24,13 @@ struct main0_out
float4 FragColor [[color(0)]]; float4 FragColor [[color(0)]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{
for (uint i = 0; i < N; dst[i] = src[i], i++);
}
fragment main0_out main0(main0_in in [[stage_in]]) fragment main0_out main0(main0_in in [[stage_in]])
{ {
main0_out out = {}; main0_out out = {};

View File

@ -5,6 +5,8 @@
using namespace metal; using namespace metal;
constant float4 _20[2] = {float4(10.0), float4(20.0)};
struct main0_in struct main0_in
{ {
float4 vInput1 [[attribute(1)]]; float4 vInput1 [[attribute(1)]];
@ -16,7 +18,7 @@ struct main0_out
float4 gl_Position [[position]]; float4 gl_Position [[position]];
}; };
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment. // Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
template<typename T, uint N> template<typename T, uint N>
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N]) void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
{ {
@ -25,7 +27,7 @@ void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
void test(thread float4 (&SPIRV_Cross_return_value)[2]) void test(thread float4 (&SPIRV_Cross_return_value)[2])
{ {
SPIRV_Cross_return_value = {float4(10.0), float4(20.0)}; spvArrayCopy(SPIRV_Cross_return_value, _20);
} }
void test2(thread float4 (&SPIRV_Cross_return_value)[2], thread float4& vInput0, thread float4& vInput1) void test2(thread float4 (&SPIRV_Cross_return_value)[2], thread float4& vInput0, thread float4& vInput1)

View File

@ -72,6 +72,7 @@ string CompilerMSL::compile()
backend.use_typed_initializer_list = true; backend.use_typed_initializer_list = true;
backend.native_row_major_matrix = false; backend.native_row_major_matrix = false;
backend.flexible_member_array_supported = false; backend.flexible_member_array_supported = false;
backend.can_declare_arrays_inline = false;
backend.can_return_array = false; backend.can_return_array = false;
replace_illegal_names(); replace_illegal_names();
@ -913,7 +914,7 @@ void CompilerMSL::emit_custom_functions()
case SPVFuncImplArrayCopy: case SPVFuncImplArrayCopy:
statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via " statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via "
"assignment. "); "assignment.");
statement("template<typename T, uint N>"); statement("template<typename T, uint N>");
statement("void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])"); statement("void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])");
begin_scope(); begin_scope();
@ -1135,6 +1136,34 @@ void CompilerMSL::declare_undefined_values()
statement(""); statement("");
} }
void CompilerMSL::declare_constant_arrays()
{
// MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to
// global constants directly, so we are able to use constants as variable expressions.
bool emitted = false;
for (auto &id : ids)
{
if (id.get_type() == TypeConstant)
{
auto &c = id.get<SPIRConstant>();
if (c.specialization)
continue;
auto &type = get<SPIRType>(c.constant_type);
if (!type.array.empty())
{
auto name = to_name(c.self);
statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
emitted = true;
}
}
}
if (emitted)
statement("");
}
void CompilerMSL::emit_resources() void CompilerMSL::emit_resources()
{ {
// Output non-interface structs. These include local function structs // Output non-interface structs. These include local function structs
@ -1171,6 +1200,7 @@ void CompilerMSL::emit_resources()
} }
} }
declare_constant_arrays();
declare_undefined_values(); declare_undefined_values();
// Output interface structs. // Output interface structs.
@ -1747,10 +1777,7 @@ bool CompilerMSL::maybe_emit_input_struct_assignment(uint32_t id_lhs, uint32_t i
void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id) void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
{ {
// Assignment from an array initializer is fine. // Assignment from an array initializer is fine.
if (ids[rhs_id].get_type() != TypeConstant) statement("spvArrayCopy(", lhs, ", ", to_expression(rhs_id), ");");
statement("spvArrayCopy(", lhs, ", ", to_expression(rhs_id), ");");
else
statement(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,
@ -1759,15 +1786,23 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
// Returns whether the struct assignment was emitted. // Returns whether the struct assignment was emitted.
bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
{ {
// Assignment from an array initializer is fine.
if (ids[id_rhs].get_type() == TypeConstant)
return false;
// We only care about assignments of an entire array // We only care about assignments of an entire array
auto &type = expression_type(id_rhs); auto &type = expression_type(id_rhs);
if (type.array.size() == 0) if (type.array.size() == 0)
return false; return false;
auto *var = maybe_get<SPIRVariable>(id_lhs);
if (ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration)
{
// Special case, if we end up declaring a variable when assigning the constant array,
// we can avoid the copy by directly assigning the constant expression.
// This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely
// the compiler will be able to optimize the spvArrayCopy() into a constant LUT.
// After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately.
statement(to_expression(id_lhs), " = ", constant_expression(get<SPIRConstant>(id_rhs)), ";");
return true;
}
// Ensure the LHS variable has been declared // Ensure the LHS variable has been declared
auto *p_v_lhs = maybe_get_backing_variable(id_lhs); auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
if (p_v_lhs) if (p_v_lhs)
@ -3566,11 +3601,24 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
// Get the result type of the RHS. Since this is run as a pre-processing stage, // 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. // we must extract the result type directly from the Instruction, rather than the ID.
uint32_t id_rhs = args[1]; uint32_t id_rhs = args[1];
uint32_t type_id_rhs = result_types[id_rhs];
if ((compiler.ids[id_rhs].get_type() != TypeConstant) && type_id_rhs &&
compiler.is_array(compiler.get<SPIRType>(type_id_rhs)))
return SPVFuncImplArrayCopy;
const SPIRType *type = nullptr;
if (compiler.ids[id_rhs].get_type() != TypeNone)
{
// Could be a constant, or similar.
type = &compiler.expression_type(id_rhs);
}
else
{
// Or ... an expression.
if (result_types[id_rhs] != 0)
type = &compiler.get<SPIRType>(result_types[id_rhs]);
}
if (type && compiler.is_array(*type))
return SPVFuncImplArrayCopy;
else
return SPVFuncImplNone;
break; break;
} }

View File

@ -208,6 +208,7 @@ protected:
std::string to_qualifiers_glsl(uint32_t id) override; std::string to_qualifiers_glsl(uint32_t id) override;
void replace_illegal_names() override; void replace_illegal_names() override;
void declare_undefined_values() override; void declare_undefined_values() override;
void declare_constant_arrays();
bool is_non_native_row_major_matrix(uint32_t id) override; bool is_non_native_row_major_matrix(uint32_t id) override;
bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type) override; std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type) override;